QT开发笔记(多媒体)

news/2024/5/20 23:53:45/文章来源:https://blog.csdn.net/qq_42700289/article/details/129701516

多媒体

多媒体(Multimedia)是多种媒体的综合,一般包括文本,声音和图像等多种媒体形式。

在计算机系统中,多媒体指组合两种或两种以上媒体的一种人机交互式信息交流和传播媒体。
使用的媒体包括文字、图片、照片、声音、动画和影片,以及程式所提供的互动功能。
Qt 的多媒体模块提供了音频、视频、录音、摄像头拍照和录像等功能。本章将介绍 Qt 多
媒体的功能和使用。

Qt 多媒体简介

Qt 从 4.4 版本开始提供的一套多媒体框架,提供多媒体回放的功能。在 Qt 4.6 中实现多媒
体播放图形界面主要依赖 phonon 框架。phonon 最初是 一个 源于 KDE 的项目,为使用音频和
视频的应用程序开发提供的一个框架。应用程序不用去管多媒体播放是通过什么实现的(如

gstreamer、xine),只需调用相应的接口就行,但这中间需要一个中转,被称为 backend。Qt 也
是通过 phonon 来实现跨平台的多媒体播放。

从 Qt5 开始,Qt 就弃用了 phonon,直接使用 Qt Multimedia 模块。我们可以 Qt Multimedia

模块来提供的类实现跨平台的多媒体播放了。使用 Qt Multimedia 就不需要中转了,但是底层还
是需要多媒体插件实现的。Qt 只是提供多媒体接口,播放多媒体实际上是通过多媒体插件实现
的,我们不需要管这些插件是什么,Qt 在不同平台使用的多媒体插件不同。本章将会介绍如何
在 Windows 和 Linux 安装多媒体插件,Mac 系统不考虑,笔者条件有限!
Qt 多媒体模块提供了很多类,主要有 QMediaPlayer,QSound、QSoundEffect、QAudioOutput、

QAudioInput、QAudioRecorder、QVideoWidget 等等。类太多了不一一作解释,可以直接复制名
字到 Qt 的帮助文档里查看该解释。可以从名称大概了解它们是什么意思,具体类的使用直接看
本章的例子。

想要在 Qt 里使用使用 Qt 多媒体模块,需要在 pro 项目文件里添加如下语句。
QT += multimedia

注意:Qt 中的音乐播放器与视频播放器需要在 Ubuntu 里安装媒体解码器才能实现播放。

 Ubuntu16 / Ubuntu18,需要安装以下插件。播放音乐需要安装 Gst 解码插件。需要在终端
输入如下指令,注意不要复制错误了,下面指令已经在 Ubuntu16/Ubuntu18 测试成功,如
果读者 Ubuntu 没有配置网络与源服务器,这些导致安装不成功与本教程无关,确实需要
读者好好打下 Ubuntu 操作的基础了!

sudo apt-get install gstreamer1.0-plugins-base gstreamer1.0-plugins-bad gstreamer1.0-plugins-good 
gstreamer1.0-plugins-ugly gstreamer1.0-pulseaudio gstreamer1.0-libav 

 Windows 需要安装如 LAVFilters 解码器,只需要百度 LAVFilters,找到 LAVFilters 官网下载此
软件即可,当然本教程的资料会提供一份 LAVFilters 的安装包。点击页脚下方的程序下载链
接跳转到下载本教程所有资料下载地址处,在顶层目录下。

音效文件播放

播放音效文件,比如简短的提示音(按键音等),可以使用 Qt 的 QSoundEffect 和 QSound

类来播放。
Qt 的 QSoundEffect 和 QSound 类主要区别是 QSoun(d 异步方式播放)只能播放本地的 WAV

音效文件(WAV 音效文件是 PC 机上最为流行的声音文件格式,但其文件尺寸较大,多用于存
储简短的声音片段,具有低延时性,不失真的特点),QSoundEffect 不仅可以播放网络文件,也
可以播放本地音效文件,播放网络的文件一般使用到 QUrl 链接。

应用实例

本例目的:了解 QSound 类的使用。

例 13_button_sound,按钮音效测试(难度:一般)。项目路径为 Qt/2/13_button_sound。本
例大体流程,通过点击一个按钮,然后使用 QSound 来播放音效文件,模仿按键按下的声音。

项目文件 13_button_sound.pro 文件第一行添加的代码部分如下。

13_button_sound.pro 编程后的代码

1 QT += core gui multimedia 2 3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 5 CONFIG += c++11 6 7 # The following define makes your compiler emit warnings if you use 8 # any Qt feature that has been marked deprecated (the exact warnings 9 # depend on your compiler). Please consult the documentation of the 10 # deprecated API in order to know how to port your code away from it. 11 DEFINES += QT_DEPRECATED_WARNINGS 12 13 # You can also make your code fail to compile if it uses deprecated APIs. 14 # In order to do so, uncomment the following line. 15 # You can also select to disable deprecated APIs only up to a certain 
version of Qt. 16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the 
APIs deprecated before Qt 6.0.0 17 18 SOURCES += \ 19 main.cpp \ 20 mainwindow.cpp 21 22 HEADERS += \ 23 mainwindow.h 24 25 # Default rules for deployment. 26 qnx: target.path = /tmp/$${TARGET}/bin 27 else: unix:!android: target.path = /opt/$${TARGET}/bin 28 !isEmpty(target.path): INSTALLS += target 29 30 RESOURCES += \ 31 src.qrc 

在头文件“mainwindow.h”具体代码如下。

mainwindow.h 编程后的代码

 /****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName 13_button_sound * @brief mainwindow.h * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-04-20 *******************************************************************/ 1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include <QMainWindow> 5 #include <QSound> 6 #include <QPushButton> 7 8 class MainWindow : public QMainWindow 9 { 10 Q_OBJECT 11 12 public: 13 MainWindow(QWidget *parent = nullptr); 14 ~MainWindow(); 15 16 private: 17 /* 按钮 */ 18 QPushButton *pushButton; 19 20 private slots: 21 /* 按钮点击槽函数 */ 22 void pushButtonClicked(); 23 24 }; 25 #endif // MAINWINDOW_H 26 

头文件里主要是声明界面使用的一个按钮,及按钮槽函数。

在源文件“mainwindow.cpp”具体代码如下。

mainwindow.cpp 编程后的代码

 /****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName 13_button_sound * @brief mainwindow.cpp * @author Deng Zhimao * @email 1252699831@qq.com 
* @net www.openedv.com * @date 2021-04-20 *******************************************************************/ 1 #include "mainwindow.h" 2 3 MainWindow::MainWindow(QWidget *parent) 4 : QMainWindow(parent) 5 { 6 /* 设置主窗体的位置与大小 */ 7 this->setGeometry(0, 0, 800, 480); 8 9 /* 实例化按钮 */ 10 pushButton = new QPushButton(this); 11 12 /* 设置按钮文本 */ 13 pushButton->setText("按钮音效测试"); 14 15 /* 设置按钮的位置与大小 */ 16 pushButton->setGeometry(340, 220, 120, 40); 17 18 /* 信号槽连接 */ 19 connect(pushButton, SIGNAL(clicked()), 20 this, SLOT(pushButtonClicked())); 21 } 22 23 MainWindow::~MainWindow() 24 { 25 } 26 27 void MainWindow::pushButtonClicked() 28 { 29 /* 异步的方式播放 */ 30 QSound::play(":/audio/bell.wav"); 31 } 

第 30 行,直接使用 QSound 的静态函数 play()播放,这种播放方式是异步的,可以多次点
击按钮连续听到点击的声音。

程序运行效果

单击按钮后,可以听到播放 1 秒左右的叮咚声,用此方法来模拟单击按钮声音效果。

在这里插入图片描述

音乐播放器

QMediaPlayer 类是一个高级媒体播放类。它可以用来播放歌曲、电影和网络广播等内容。
一般用于播放 mp3 和 mp4 等等媒体文件。QMediaPlayer 类常常与 QMediaPlaylist 类一起使用。
可以很轻松的设计一个自己喜欢的音乐播放器与视频播放器。
QMediaPlayer 提供了很多信号,我们可以使用这些信号来完成音乐播放器的一系列操作,
比如媒体状态改变的信号 stateChanged(QMediaPlayer::State state),判断这个 state 的状态就可以
知道什么时候媒体暂停、播放、停止了。Qt 在媒体播放类已经提供了很多功能函数给我们使用,
像直接使用 play()函数就可以实现音乐文件的播放,前提我们需要知道媒体文件的路径。pause()

函数可以直接暂停媒体播放等等,这些都可以在 Qt 帮助文档里查看 QMediaPlayer 类的使用方
法就可以知道。不再一一列出。

应用实例

本例设计一个比较好看的音乐播放器,界面是笔者模仿网上的一个音乐播放器的界面,并
非笔者原创界面,只是笔者用 Qt 实现了网上的一个好看的音乐播放器界面。其中本例有些功能
并没有完善,比如播放模式、没有加音量控制等。这些可以由读者自由完善,比较简单。

本例目的:音乐播放器的设计与使用。

例 14_musicplayer,音乐播放器(难度:中等)。项目路径为 Qt/2/14_musicplayer。注意本
例有用到 qss 样式文件,关于如何添加资源文件与 qss 文件请参考 7.1.3 小节。音乐播放器的功
能这些都为大家所熟知,不用笔者介绍了。

项目文件 14_musicplayer.pro 文件第一行添加的代码部分如下。

14_musicplayer.pro 编程后的代码

1 QT += core gui multimedia 2 3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 5 CONFIG += c++11 6 7 # The following define makes your compiler emit warnings if you use 8 # any Qt feature that has been marked deprecated (the exact warnings 9 # depend on your compiler). Please consult the documentation of the 10 # deprecated API in order to know how to port your code away from it. 11 DEFINES += QT_DEPRECATED_WARNINGS 12 13 # You can also make your code fail to compile if it uses deprecated APIs. 14 # In order to do so, uncomment the following line. 15 # You can also select to disable deprecated APIs only up to a certain 
version of Qt. 16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the 
APIs deprecated before Qt 6.0.0 17 18 SOURCES += \ 19 main.cpp \ 20 mainwindow.cpp 21 22 HEADERS += \ 23 mainwindow.h 24 25 # Default rules for deployment. 26 qnx: target.path = /tmp/$${TARGET}/bin 27 else: unix:!android: target.path = /opt/$${TARGET}/bin 28 !isEmpty(target.path): INSTALLS += target 29 30 RESOURCES += \ 31 res.qrc 

在头文件“mainwindow.h”具体代码如下。

mainwindow.h 编程后的代码

 /****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName 14_musicplayer * @brief mainwindow.h * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-04-20 *******************************************************************/
1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include <QMainWindow> 5 #include <QMediaPlayer> 6 #include <QMediaPlaylist> 7 #include <QPushButton> 8 #include <QSlider> 9 #include <QVBoxLayout> 10 #include <QHBoxLayout> 11 #include <QListWidget> 12 #include <QLabel> 13 #include <QSpacerItem> 14 #include <QDebug> 15 16 /* 媒体信息结构体 */ 17 struct MediaObjectInfo { 18 /* 用于保存歌曲文件名 */ 19 QString fileName; 20 /* 用于保存歌曲文件路径 */ 21 QString filePath; 22 }; 23 24 class MainWindow : public QMainWindow 25 { 26 Q_OBJECT 27 28 public: 29 MainWindow(QWidget *parent = nullptr); 30 ~MainWindow(); 31 32 private: 33 /* 媒体播放器,用于播放音乐 */ 34 QMediaPlayer *musicPlayer; 35 36 /* 媒体列表 */ 37 QMediaPlaylist *mediaPlaylist; 38 39 /* 音乐列表 */ 40 QListWidget *listWidget; 41 42 /* 播放进度条 */ 43 QSlider *durationSlider; 44 45 /* 音乐播放器按钮 */ 46 QPushButton *pushButton[7]; 47 48 /* 垂直布局 */ 49 QVBoxLayout *vBoxLayout[3]; 50 51 /* 水平布局 */ 52 QHBoxLayout *hBoxLayout[4]; 53 54 /* 垂直容器 */ 55 QWidget *vWidget[3]; 56 57 /* 水平容器 */ 58 QWidget *hWidget[4]; 59 60 /* 标签文本 */ 61 QLabel *label[4]; 62 63 /* 用于遮罩 */ 64 QWidget *listMask; 65 66 /* 音乐布局函数 */ 67 void musicLayout(); 68 69 /* 主窗体大小重设大小函数重写 */ 70 void resizeEvent(QResizeEvent *event); 71 72 /* 媒体信息存储 */ 73 QVector<MediaObjectInfo> mediaObjectInfo; 74 75 /* 扫描歌曲 */ 76 void scanSongs(); 77 78 /* 媒体播放器类初始化 */ 79 void mediaPlayerInit(); 80 81 private slots: 82 /* 播放按钮点击 */ 83 void btn_play_clicked(); 84 85 /* 下一曲按钮点击*/86 void btn_next_clicked(); 87 88 /* 上一曲按钮点击 */ 89 void btn_previous_clicked(); 90 91 /* 媒体状态改变 */ 92 void mediaPlayerStateChanged(QMediaPlayer::State); 93 94 /* 列表单击 */ 95 void listWidgetCliked(QListWidgetItem*); 96 97 /* 媒体列表项改变 */ 98 void mediaPlaylistCurrentIndexChanged(int); 99 100 /* 媒体总长度改变 */ 101 void musicPlayerDurationChanged(qint64); 102 103 /* 媒体播放位置改变 */ 104 void mediaPlayerPositionChanged(qint64); 105 106 /* 播放进度条松开 */ 107 void durationSliderReleased(); 108 }; 109 #endif // MAINWINDOW_H 

头文件里主要是声明界面所使用的元素及一些槽函数。

在源文件“mainwindow.cpp”具体代码如下。

mainwindow.cpp 编程后的代码

 /****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName 14_musicplayer * @brief mainwindow.cpp * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-04-20 *******************************************************************/ 1 #include "mainwindow.h" 2 #include <QCoreApplication> 3 #include <QFileInfoList> 4 #include <QDir> 5 6 MainWindow::MainWindow(QWidget *parent) 7 : QMainWindow(parent) 8 { 9 /* 布局初始化 */ 10 musicLayout(); 11 12 /* 媒体播放器初始化 */ 13 mediaPlayerInit(); 14 15 /* 扫描歌曲 */ 16 scanSongs(); 17 18 /* 按钮信号槽连接 */ 19 connect(pushButton[0], SIGNAL(clicked()), 20 this, SLOT(btn_previous_clicked())); 21 connect(pushButton[1], SIGNAL(clicked()), 22 this, SLOT(btn_play_clicked())); 23 connect(pushButton[2], SIGNAL(clicked()), 24 this, SLOT(btn_next_clicked())); 25 26 /* 媒体信号槽连接 */ 27 connect(musicPlayer, 28 SIGNAL(stateChanged(QMediaPlayer::State)), 29 this, 30 SLOT(mediaPlayerStateChanged(QMediaPlayer::State))); 31 connect(mediaPlaylist, 32 SIGNAL(currentIndexChanged(int)), 33 this, 34 SLOT(mediaPlaylistCurrentIndexChanged(int))); 35 connect(musicPlayer, SIGNAL(durationChanged(qint64)), 36 this, 37 SLOT(musicPlayerDurationChanged(qint64))); 38 connect(musicPlayer, 39 SIGNAL(positionChanged(qint64)), 40 this, 41 SLOT(mediaPlayerPositionChanged(qint64))); 42 43 /* 列表信号槽连接 */ 44 connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)), 45 this, SLOT(listWidgetCliked(QListWidgetItem*))); 46 47 /* slider 信号槽连接 */ 48 connect(durationSlider, SIGNAL(sliderReleased()), 49 this, SLOT(durationSliderReleased())); 51 /* 失去焦点 */ 52 this->setFocus(); 53 } 54 55 void MainWindow::musicLayout() 56 { 57 /* 设置位置与大小,这里固定为 800, 480 */ 58 this->setGeometry(0, 0, 800, 480); 59 QPalette pal; 60 61 /* 按钮 */ 62 for (int i = 0; i < 7; i++) 63 pushButton[i] = new QPushButton(); 64 65 /* 标签 */ 66 for (int i = 0; i < 4; i++) 67 label[i] = new QLabel(); 68 69 for (int i = 0; i < 3; i++) { 70 /* 垂直容器 */ 71 vWidget[i] = new QWidget(); 72 vWidget[i]->setAutoFillBackground(true); 73 /* 垂直布局 */ 74 vBoxLayout[i] = new QVBoxLayout(); 75 } 76 77 for (int i = 0; i < 4; i++) { 78 /* 水平容器 */ 79 hWidget[i] = new QWidget(); 80 hWidget[i]->setAutoFillBackground(true); 81 /* 水平布局 */ 82 hBoxLayout[i] = new QHBoxLayout(); 83 } 84 85 /* 播放进度条 */ 86 durationSlider = new QSlider(Qt::Horizontal); 87 durationSlider->setMinimumSize(300, 15); 88 durationSlider->setMaximumHeight(15); 89 durationSlider->setObjectName("durationSlider"); 90 91 /* 音乐列表 */ 92 listWidget = new QListWidget(); 93 listWidget->setObjectName("listWidget"); 94 listWidget->resize(310, 265); 95 listWidget->setVerticalScrollBarPolicy( 96 Qt::ScrollBarAlwaysOff); 97 listWidget->setHorizontalScrollBarPolicy( 98 Qt::ScrollBarAlwaysOff); 99 100 /* 列表遮罩 */ 101 listMask = new QWidget(listWidget); 102 listMask->setMinimumSize(310, 50); 103 listMask->setMinimumHeight(50); 104 listMask->setObjectName("listMask"); 105 listMask->setGeometry(0, 106 listWidget->height() - 50, 107 310, 108 50); 109 110 /* 设置对象名称 */ 111 pushButton[0]->setObjectName("btn_previous"); 112 pushButton[1]->setObjectName("btn_play"); 113 pushButton[2]->setObjectName("btn_next"); 114 pushButton[3]->setObjectName("btn_favorite"); 115 pushButton[4]->setObjectName("btn_mode"); 116 pushButton[5]->setObjectName("btn_menu"); 117 pushButton[6]->setObjectName("btn_volume"); 118 119 /* 设置按钮属性 */ 120 pushButton[1]->setCheckable(true); 121 pushButton[3]->setCheckable(true); 122 123 /* H0 布局 */ 124 vWidget[0]->setMinimumSize(310, 480); 125 vWidget[0]->setMaximumWidth(310); 126 vWidget[1]->setMinimumSize(320, 480); 127 QSpacerItem *hSpacer0 = new 128 QSpacerItem(70, 480, 129 QSizePolicy::Minimum, 130 QSizePolicy::Maximum); 131 132 QSpacerItem *hSpacer1 = new 133 QSpacerItem(65, 480, 134 QSizePolicy::Minimum, 135 QSizePolicy::Maximum); 136 137 QSpacerItem *hSpacer2 = new 138 QSpacerItem(60, 480, 139 QSizePolicy::Minimum, 140 QSizePolicy::Maximum); 141 142 hBoxLayout[0]->addSpacerItem(hSpacer0); 143 hBoxLayout[0]->addWidget(vWidget[0]); 144 hBoxLayout[0]->addSpacerItem(hSpacer1); 145 hBoxLayout[0]->addWidget(vWidget[1]); 146 hBoxLayout[0]->addSpacerItem(hSpacer2); 147 hBoxLayout[0]->setContentsMargins(0, 0, 0, 0); 148 149 hWidget[0]->setLayout(hBoxLayout[0]); 150 setCentralWidget(hWidget[0]); 151 152 /* V0 布局 */ 153 listWidget->setMinimumSize(310, 265); 154 hWidget[1]->setMinimumSize(310, 80); 155 hWidget[1]->setMaximumHeight(80); 156 label[0]->setMinimumSize(310, 95); 157 label[0]->setMaximumHeight(95); 158 QSpacerItem *vSpacer0 = new 159 QSpacerItem(310, 10, 160 QSizePolicy::Minimum, 161 QSizePolicy::Maximum); 162 QSpacerItem *vSpacer1 = new 163 QSpacerItem(310, 30, 164 QSizePolicy::Minimum, 165 QSizePolicy::Minimum); 166 vBoxLayout[0]->addWidget(label[0]); 167 vBoxLayout[0]->addWidget(listWidget); 168 vBoxLayout[0]->addSpacerItem(vSpacer0); 169 vBoxLayout[0]->addWidget(hWidget[1]); 170 vBoxLayout[0]->addSpacerItem(vSpacer1); 171 vBoxLayout[0]->setContentsMargins(0, 0, 0, 0); 172 173 vWidget[0]->setLayout(vBoxLayout[0]); 174 175 /* H1 布局 */ 176 for (int i = 0; i < 3; i++) { 177 pushButton[i]->setMinimumSize(80, 80); 178 } 179 QSpacerItem *hSpacer3 = new 180 QSpacerItem(40, 80, 181 QSizePolicy::Expanding, 182 QSizePolicy::Expanding); 183 QSpacerItem *hSpacer4 = new 184 QSpacerItem(40, 80, 185 QSizePolicy::Expanding, 186 QSizePolicy::Expanding); 187 hBoxLayout[1]->addWidget(pushButton[0]); 188 hBoxLayout[1]->addSpacerItem(hSpacer3); 189 hBoxLayout[1]->addWidget(pushButton[1]); 190 hBoxLayout[1]->addSpacerItem(hSpacer4); 191 hBoxLayout[1]->addWidget(pushButton[2]); 192 hBoxLayout[1]->setContentsMargins(0, 0, 0, 0); 193 194 hWidget[1]->setLayout(hBoxLayout[1]); 195 196 /* V1 布局 */ 197 QSpacerItem *vSpacer2 = new 198 QSpacerItem(320, 40, 199 QSizePolicy::Minimum, 200 QSizePolicy::Maximum); 201 QSpacerItem *vSpacer3 = new 202 QSpacerItem(320, 20, 203 QSizePolicy::Minimum, 204 QSizePolicy::Maximum); 205 QSpacerItem *vSpacer4 = new 206 QSpacerItem(320, 30, 207 QSizePolicy::Minimum, 208 QSizePolicy::Minimum); 209 label[1]->setMinimumSize(320, 320); 210 QImage Image; 211 Image.load(":/images/cd.png"); 212 QPixmap pixmap = QPixmap::fromImage(Image); 213 int with = 320; 214 int height = 320; 215 QPixmap fitpixmap = 216 pixmap.scaled(with, height, 217 Qt::IgnoreAspectRatio, 218 Qt::SmoothTransformation); 219 label[1]->setPixmap(fitpixmap); 220 label[1]->setAlignment(Qt::AlignCenter); 221 vWidget[2]->setMinimumSize(300, 80); 222 vWidget[2]->setMaximumHeight(80); 223 vBoxLayout[1]->addSpacerItem(vSpacer2); 224 vBoxLayout[1]->addWidget(label[1]); 225 vBoxLayout[1]->addSpacerItem(vSpacer3); 226 vBoxLayout[1]->addWidget(durationSlider); 227 vBoxLayout[1]->addWidget(vWidget[2]); 228 vBoxLayout[1]->addSpacerItem(vSpacer4); 229 vBoxLayout[1]->setContentsMargins(0, 0, 0, 0); 230 231 vWidget[1]->setLayout(vBoxLayout[1]); 232 233 /* V2 布局 */ 234 QSpacerItem *vSpacer5 = new 235 QSpacerItem(300, 10, 236 QSizePolicy::Minimum, 237 QSizePolicy::Maximum); 238 hWidget[2]->setMinimumSize(320, 20); 239 hWidget[3]->setMinimumSize(320, 60); 240 vBoxLayout[2]->addWidget(hWidget[2]); 241 vBoxLayout[2]->addSpacerItem(vSpacer5); 242 vBoxLayout[2]->addWidget(hWidget[3]); 243 vBoxLayout[2]->setContentsMargins(0, 0, 0, 0); 244 245 vWidget[2]->setLayout(vBoxLayout[2]); 246 247 /* H2 布局 */ 248 label[2]->setText("00:00"); 249 label[3]->setText("00:00"); 250 QFont font; 251 252 font.setPixelSize(10); 253 254 /* 设置标签文本 */ 255 label[0]->setText("Q Music,Enjoy it!"); 256 label[2]->setText("00:00"); 257 label[3]->setText("00:00"); 258 label[2]->setSizePolicy(QSizePolicy::Expanding, 259 QSizePolicy::Expanding); 260 label[3]->setSizePolicy(QSizePolicy::Expanding, 261 QSizePolicy::Expanding); 262 label[3]->setAlignment(Qt::AlignRight); 263 label[2]->setAlignment(Qt::AlignLeft); 264 label[2]->setFont(font); 265 label[3]->setFont(font); 266 267 pal.setColor(QPalette::WindowText, Qt::white); 268 label[0]->setPalette(pal); 269 label[2]->setPalette(pal); 270 label[3]->setPalette(pal); 271 272 hBoxLayout[2]->addWidget(label[2]); 273 hBoxLayout[2]->addWidget(label[3]); 274 275 hBoxLayout[2]->setContentsMargins(0, 0, 0, 0); 276 hWidget[2]->setLayout(hBoxLayout[2]); 277 278 /* H3 布局 */ 279 QSpacerItem *hSpacer5 = new 280 QSpacerItem(0, 60, 281 QSizePolicy::Minimum, 282 QSizePolicy::Maximum); 283 QSpacerItem *hSpacer6 = new 284 QSpacerItem(80, 60, 285 QSizePolicy::Maximum, 286 QSizePolicy::Maximum); 287 QSpacerItem *hSpacer7 = new 288 QSpacerItem(80, 60, 289 QSizePolicy::Maximum, 290 QSizePolicy::Maximum); 291 QSpacerItem *hSpacer8 = new 292 QSpacerItem(80, 60, 293 QSizePolicy::Maximum, 294 QSizePolicy::Maximum); 295 QSpacerItem *hSpacer9 = new 296 QSpacerItem(0, 60, 297 QSizePolicy::Minimum, 298 QSizePolicy::Maximum); 299 300 for (int i = 3; i < 7; i++) { 301 pushButton[i]->setMinimumSize(25, 25); 302 pushButton[i]->setMaximumSize(25, 25); 303 } 304 305 hBoxLayout[3]->addSpacerItem(hSpacer5); 306 hBoxLayout[3]->addWidget(pushButton[3]); 307 hBoxLayout[3]->addSpacerItem(hSpacer6); 308 hBoxLayout[3]->addWidget(pushButton[4]); 309 hBoxLayout[3]->addSpacerItem(hSpacer7); 310 hBoxLayout[3]->addWidget(pushButton[5]); 311 hBoxLayout[3]->addSpacerItem(hSpacer8); 312 hBoxLayout[3]->addWidget(pushButton[6]); 313 hBoxLayout[3]->addSpacerItem(hSpacer9); 314 hBoxLayout[3]->setContentsMargins(0, 0, 0, 0); 315 hBoxLayout[3]->setAlignment(Qt::AlignHCenter); 316 317 hWidget[3]->setLayout(hBoxLayout[3]); 318 319 //hWidget[0]->setStyleSheet("background-color:red"); 320 //hWidget[1]->setStyleSheet("background-color:#ff5599"); 321 //hWidget[2]->setStyleSheet("background-color:#ff55ff"); 322 //hWidget[3]->setStyleSheet("background-color:black"); 323 //vWidget[0]->setStyleSheet("background-color:#555555"); 324 //vWidget[1]->setStyleSheet("background-color:green"); 325 //vWidget[2]->setStyleSheet("background-color:gray"); 326 327 } 328 329 MainWindow::~MainWindow() 330 { 331 } 332 333 void MainWindow::btn_play_clicked() 334 { 335 int state = musicPlayer->state(); 336 337 switch (state) { 338 case QMediaPlayer::StoppedState: 339 /* 媒体播放 */ 340 musicPlayer->play(); 341 break; 342 343 case QMediaPlayer::PlayingState: 344 /* 媒体暂停 */ 345 musicPlayer->pause(); 346 break; 347 348 case QMediaPlayer::PausedState: 349 musicPlayer->play(); 350 break; 351 } 352 } 353 354 void MainWindow::btn_next_clicked() 355 { 356 musicPlayer->stop(); 357 int count = mediaPlaylist->mediaCount(); 358 if (0 == count) 359 return; 360 361 /* 列表下一个 */ 362 mediaPlaylist->next(); 363 musicPlayer->play(); 364 } 365 366 void MainWindow::btn_previous_clicked() 367 { 368 musicPlayer->stop(); 369 int count = mediaPlaylist->mediaCount(); 370 if (0 == count) 371 return; 372 373 /* 列表上一个 */ 374 mediaPlaylist->previous(); 375 musicPlayer->play(); 376 } 377 378 void MainWindow::mediaPlayerStateChanged( 379 QMediaPlayer::State 380 state) 381 { 382 switch (state) { 383 case QMediaPlayer::StoppedState: 384 pushButton[1]->setChecked(false); 385 break; 386 387 case QMediaPlayer::PlayingState: 388 pushButton[1]->setChecked(true); 389 break; 390 391 case QMediaPlayer::PausedState: 392 pushButton[1]->setChecked(false); 393 break; 394 } 395 } 396 397 void MainWindow::listWidgetCliked(QListWidgetItem *item) 398 { 399 musicPlayer->stop(); 400 mediaPlaylist->setCurrentIndex(listWidget->row(item)); 401 musicPlayer->play(); 402 } 403 404 void MainWindow::mediaPlaylistCurrentIndexChanged( 405 int index) 406 { 407 if (-1 == index) 408 return; 409 410 /* 设置列表正在播放的项 */ 411 listWidget->setCurrentRow(index); 412 } 413 414 void MainWindow::musicPlayerDurationChanged( 415 qint64 duration) 416 { 417 durationSlider->setRange(0, duration / 1000); 418 int second = duration / 1000; 419 int minute = second / 60; 420 second %= 60; 421 422 QString mediaDuration; 423 mediaDuration.clear(); 424 425 if (minute >= 10) 426 mediaDuration = QString::number(minute, 10); 427 else 428 mediaDuration = "0" + QString::number(minute, 10); 429 430 if (second >= 10) 431 mediaDuration = mediaDuration 432 + ":" + QString::number(second, 10); 433 else 434 mediaDuration = mediaDuration 435 + ":0" + QString::number(second, 10); 436 437 /* 显示媒体总长度时间 */ 438 label[3]->setText(mediaDuration); 439 } 440 441 void MainWindow::mediaPlayerPositionChanged( 442 qint64 position) 443 { 444 if (!durationSlider->isSliderDown()) 445 durationSlider->setValue(position/1000); 446 447 int second = position / 1000; 448 int minute = second / 60; 449 second %= 60; 450 451 QString mediaPosition; 452 mediaPosition.clear(); 453 454 if (minute >= 10) 455 mediaPosition = QString::number(minute, 10); 456 else 457 mediaPosition = "0" + QString::number(minute, 10); 458 459 if (second >= 10) 460 mediaPosition = mediaPosition 461 + ":" + QString::number(second, 10); 462 else 463 mediaPosition = mediaPosition 464 + ":0" + QString::number(second, 10); 465 466 /* 显示现在播放的时间 */ 467 label[2]->setText(mediaPosition); 468 } 469 470 void MainWindow::resizeEvent(QResizeEvent *event) 471 { 472 Q_UNUSED(event); 473 listMask->setGeometry(0, 474 listWidget->height() - 50, 475 310, 476 50); 477 } 478 479 void MainWindow::durationSliderReleased() 480 { 481 /* 设置媒体播放的位置 */ 482 musicPlayer->setPosition(durationSlider->value() * 1000); 483 } 484 485 void MainWindow::scanSongs() 486 { 487 QDir dir(QCoreApplication::applicationDirPath() 488 + "/myMusic"); 489 QDir dirbsolutePath(dir.absolutePath()); 490 /* 如果目录存在 */ 491 if (dirbsolutePath.exists()) { 492 /* 定义过滤器 */ 493 QStringList filter; 494 /* 包含所有.mp3 后缀的文件 */ 495 filter << "*.mp3"; 496 /* 获取该目录下的所有文件 */ 497 QFileInfoList files = 498 dirbsolutePath.entryInfoList(filter, QDir::Files); 499 /* 遍历 */ 500 for (int i = 0; i < files.count(); i++) { 501 MediaObjectInfo info; 502 /* 使用 utf-8 编码 */ 503 QString fileName = QString::fromUtf8(files.at(i) 504 .fileName() 505 .replace(".mp3", "") 506 .toUtf8() 507 .data()); 508 info.fileName = fileName + "\n" 509 + fileName.split("-").at(1); 510 info.filePath = QString::fromUtf8(files.at(i) 511 .filePath() 512 .toUtf8() 513 .data()); 514 /* 媒体列表添加歌曲 */ 515 if (mediaPlaylist->addMedia( 516 QUrl::fromLocalFile(info.filePath))) { 517 /* 添加到容器数组里储存 */ 518 mediaObjectInfo.append(info); 519 /* 添加歌曲名字至列表 */ 520 listWidget->addItem(info.fileName); 521 } else { 522 qDebug()<< 523 mediaPlaylist->errorString() 524 .toUtf8().data() 525 << endl; 526 qDebug()<< " Error number:" 527 << mediaPlaylist->error() 528 << endl; 529 } 530 } 531 } 532 } 533 534 void MainWindow::mediaPlayerInit() 535 { 536 musicPlayer = new QMediaPlayer(this); 537 mediaPlaylist = new QMediaPlaylist(this); 538 /* 确保列表是空的 */ 539 mediaPlaylist->clear(); 540 /* 设置音乐播放器的列表为 mediaPlaylist */ 541 musicPlayer->setPlaylist(mediaPlaylist); 542 /* 设置播放模式,Loop 是列循环 */ 543 mediaPlaylist->setPlaybackMode(QMediaPlaylist::Loop); 544 } 

第 10 行,布局初始化,第一步我们先建立好界面,确定好布局再实现功能,一般流程都这
样,布局 msuicLayout()的内容比较多,也不难,但是比较复杂,如果我们有第七章的基础,看
这种布局是没有难度的,这里就不多解释了。没有好看的布局也能完成本例。如果您喜欢这种
布局方法,您需要多花点时间去研究如何布局才好看,这些没有固定的方法,完全是一个人的
审美感。
第 13 行,媒体先初始化,初始化媒体播放器与媒体播放列表。
第 16 行,扫描歌曲,初始化本地歌曲,从固定的文件夹里找歌曲文件,这里笔者设计从固
定的文件夹里找歌曲文件。也有些读者可能会说可以自由选择文件夹吗?答案是可以的!使用

QFileDilog 类打开选择歌曲目录或者文件即可!但是在嵌入式里,一般是初始化界里面就已经
有歌曲在界面里的了,无需用户再去打开,打开歌曲这种操作是极少会使用的!
第 26 至 47 行,信号槽连接,这里使用了各种各样的信号。这里有必要说明一下,笔者设
计了单击歌曲列表就能播放歌曲了。如果在电脑上可能有些播放器软件会双击才能播放歌曲,
在嵌入式的触摸屏里,只有单击!没有双击,没有用户会双击歌曲的列表的。这些特殊的地方
我们需要在嵌入式里考虑!
第 333~376 行,点击播放按钮,上一曲,下一曲,Qt 的媒体类已经提供了 previous(),next(),

stop(),play(),pause()这些方法直接可以使用。除了实现好看的界面之外,这部分内容也是本
例实现播放器重要的部分!
其他部分是实现播放状态的切换及扩进度条的显示进度处理,请参阅源码理解。
main.cpp 内容如下,主要是加载 qss 样式文件。没有什么可讲解。

1 #include "mainwindow.h" 2 3 #include <QApplication> 4 #include <QFile> 5 6 int main(int argc, char *argv[]) 7 { 8 QApplication a(argc, argv); 9 /* 指定文件 */ 10 QFile file(":/style.qss"); 11 12 /* 判断文件是否存在 */ 13 if (file.exists() ) { 14 /* 以只读的方式打开 */ 15 file.open(QFile::ReadOnly); 16 /* 以字符串的方式保存读出的结果 */ 17 QString styleSheet = QLatin1String(file.readAll()); 18 /* 设置全局样式 */ 19 qApp->setStyleSheet(styleSheet); 20 /* 关闭文件 */ 21 file.close(); 22 } 23 24 MainWindow w; 25 w.show(); 26 return a.exec(); 27 } 

style.qss 样式文件如下。素材已经在源码处提供。注意下面的 style.qss 不能有注释!

1 QWidget { 2 background: "#25242a" 3 } 4 5 QWidget#listMask { 6 border-image: url(:/images/mask.png); 7 background-color: transparent; 8 } 9 10 QListWidget#listWidget { 11 color:white; 12 font-size: 15px; 13 border:none; 14 } 15 16 QListWidget#listWidget:item:active { 17 background: transparent; 18 } 19 20 QListWidget#listWidget:item { 21 background: transparent; 22 height:60; 23 } 24 25 QListWidget#listWidget:item:selected { 26 color:#5edcf3; 27 background: transparent; 28 } 29 30 QListWidget#listWidget:item:hover { 31 background: transparent; 32 color:#5edcf3; 33 border:none; 34 } 35 36 QPushButton#btn_play { 37 border-image:url(:/images/btn_play1.png); 38 } 39 40 QPushButton#btn_play:hover { 41 border-image:url(:/images/btn_play2.png); 42 } 43 44 QPushButton#btn_play:checked { 45 border-image:url(:/images/btn_pause1.png); 46 } 47 48 QPushButton#btn_play:checked:hover { 49 border-image:url(:/images/btn_pause2.png); 50 } 51 52 QPushButton#btn_previous { 53 border-image:url(:/images/btn_previous1.png); 54 } 55 56 QPushButton#btn_previous:hover { 57 border-image:url(:/images/btn_previous2.png); 58 } 59 60 QPushButton#btn_next { 61 border-image:url(:/images/btn_next1.png); 62 } 63 64 QPushButton#btn_next:hover { 65 border-image:url(:/images/btn_next2.png); 66 } 67 68 QPushButton#btn_favorite { 69 border-image:url(:/images/btn_favorite_no.png); 70 } 71 72 QPushButton#btn_favorite:checked { 73 border-image:url(:/images/btn_favorite_yes.png); 74 } 75 76 QPushButton#btn_menu { 77 border-image:url(:/images/btn_menu1.png); 78 } 79 80 QPushButton#btn_menu:hover { 81 border-image:url(:/images/btn_menu2.png); 82 } 83 84 QPushButton#btn_mode { 85 border-image:url(:/images/btn_listcircle1.png); 86 } 87 88 QPushButton#btn_mode:hover { 89 border-image:url(:/images/btn_listcircle2.png); 90 } 91 92 QPushButton#btn_mode { 93 border-image:url(:/images/btn_listcircle1.png); 94 } 95 96 QPushButton#btn_mode:hover { 97 border-image:url(:/images/btn_listcircle2.png); 98 } 99 100 QPushButton#btn_volume { 101 border-image:url(:/images/btn_volume1.png); 102 } 103 104 QPushButton#btn_volume:hover { 105 border-image:url(:/images/btn_volume2.png); 106 } 107 108 QSlider#durationSlider:handle:horizontal { 109 border-image:url(:/images/handle.png); 110 } 111 112 QSlider#durationSlider:sub-page:horizontal { 113 border-image:url(:/images/sub-page.png); 114 } 

程序运行效果

先点击构建项目,项目构建完成后,再将本例的 myMusic 歌曲文件夹拷贝到可执行程序的
文件夹同一级目录下,也就是 build-14_musicplayer-Desktop_Qt_5_12_9_GCC_64bit-Debug 目录
下(windows 需要进入到 debug 目录)。再点击运行,就出现歌曲在列表里,如下图,点击播放
即可播放歌曲,上一曲,下一曲也可以用。注意右下角的某些按钮功能,在本例没有继续去实
现,比如音量控制,可以直接加一个垂直方向的滑条,然后控制媒体的软件音量即可。留给读
者自由发挥,可以基于本例去开发,就当读者练习吧。本例的界面开发笔者还是比较满意的,
前面的界面都比较普通,笔者个人 Qt 开发重要的是界面与稳定性,功能是次要的!因为功能都
不难实现。

注意歌曲格式应严格为歌名 + “-” + 歌手名称.mp3,例如江南 - 林俊杰.mp3。中间的
“-”是英文字符的短横杠,这样的目的是能够将歌曲名称和歌手分开显示到界面上。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

头歌c语言实训项目-函数(2)

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 第1关&#xff1a;模拟投掷骰子游戏: 题目&#xff1a; 代码思路&#xff1a; 代码表示&#xff1a; 第…

20230322英语学习

Why Are So Many Gen Z-Ers Drawn to Old Digital Cameras? 老式数码相机&#xff1a;Z世代的复古潮流 The latest digital cameras boast ever-higher resolutions, better performance in low light, smart focusing and shake reduction – and they’re built right into …

牛客C/C++刷题笔记(五)

122、对于"int *pa[5]&#xff1b;"的描述中&#xff0c;&#xff08; &#xff09;是正确的。 123、以下叙述中正确的是&#xff08;&#xff09; C语言的源程序中对缩进没有要求,所以A选项错误。C语言中区分大小写,main函数不能写成Main或_main,所以B选项错误。一…

声声不息,新“声”报到

魅力声音大家庭总群&#xff08;10&#xff09;大玲&#xff0c;刚见到新来的四川孩儿——樱桃&#xff0c;真是太可爱了&#xff01;可不就是&#xff0c;这孩儿真是招人稀罕&#xff0c;我现在就把她拉到咱大群里“大玲” 邀请 “樱桃” 加入群聊所有人 咱们大家庭迎来了第一…

【字符串】刷题

P4173残缺的字符串心得&#xff1a;这道题&#xff0c;我觉得是不难的&#xff0c;代码逻辑很清晰&#xff0c;但是提交就是有问题最后发现两个问题&#xff1a;scanf输入字符后要用getchar() 吞回车 !!!!&#xff08;天坑用 scanf 输入时&#xff0c;不管输入什么&#xff0c;…

C语言小程序:通讯录(静态版)

哈喽各位老铁们&#xff0c;今天给大家带来一期通讯录的静态版本的实现&#xff0c;何为静态版本后面会做解释&#xff0c;话不多说&#xff0c;直接开始&#xff01;关于通讯录&#xff0c;其实也就是类似于我们手机上的通讯录一样&#xff0c;有着各种各样的功能&#xff0c;…

30天从零到1创业螺旋式

趁梦想还在&#xff0c;想去的地方&#xff0c;现在就去&#xff1b;想做的事情&#xff0c;现在就做。一开始立刻启动的你的项目&#xff0c;安排好时间计划&#xff0c;拆分微模块&#xff0c;每天花20分钟去完善产品&#xff0c;去改变世界。可以为自己的创意设置临时办公室…

RK3588平台开发系列讲解(显示篇)DP显示调试方法

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、查看 connector 状态二、强制使能/禁⽤ DP三、DPCP 读写四、Type-C 接口 Debug五、查看 DP 寄存器六、查看 VOP 状态七、查看当前显示时钟八、调整 DRM log 等级沉淀、分享、成长,让自己和他人都能有所收获!😄…

【数据结构】链队列的C语言实现

队列 1.队列的概念 队列 和栈一样&#xff0c;是一个 特殊的线性表。 队列只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。进行 插入操作 的一端称为 队尾&#xff0c;进行 删除操作 的一端称为队头。 队列中的元素遵守 先进先出(First I…

【类的继承与派生的知识点】

文章目录类的继承与派生的知识点类的继承与派生&#xff1a;类成员的访问&#xff1a;类型兼容规则&#xff1a;一个公有派生类的对象在使用上可以被当成基类的对象&#xff0c;反之不行单继承与多继承派生类的构造与析构类成员的标识与访问类的继承与派生的实验结果类型兼容规…

Baumer工业相机堡盟相机如何使用Sharpening图像锐化功能( Sharpening图像锐化功能的优点和行业应用)(C++)

项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能&#xff0c;可以实时传输高分辨率图像。此外&#xff0c;该相机还具…

【计算机网络】如何解决TCP粘包问题?

【计算机网络】如何解决TCP粘包问题&#xff1f; 文章目录【计算机网络】如何解决TCP粘包问题&#xff1f;如何理解字节流&#xff1f;如何解决粘包&#xff1f;固定长度的消息特殊字符作为边界自定义消息结构如何理解字节流&#xff1f; 之所以会说 TCP 是面向字节流的协议&a…

RK3588编译环境Ubuntu20.04编译配置-增加交换内存

迅为提供的编译环境 Ubuntu20.04 默认配置了交换内存是 9G&#xff0c;如果在编译过程中&#xff0c;因内 存不够而编译报错&#xff0c;可以参考本小节进行设置。 这里举例分配 5G 交换内存。 在开始之前&#xff0c;使用命令检查一下您的 ubuntu 的 swap 分区。 sudo swa…

Android进阶面经,面试10余家经验分享,拿到offer真不难~

前言 我们都知道面试大厂主要就是考察程序员技术方向的专业技能&#xff0c;Java开发主要考察的就是Java方面的专业技能&#xff0c;而Android岗位的 专业技能 就是Android程序员面试的重要考察方向。 大厂的招聘条件是明牌的&#xff0c;但技术这一块却难倒了大部分的人。 面…

蓝桥杯刷题冲刺 | 倒计时18天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录0.知识点1.乳草的入侵今天写 搜索题 0.知识点 DFS 设计步骤 确定该题目的状态&#xff08;包括边…

服务器boa移植

服务器boa移植 文章目录服务器boa移植1.下载boa2.解压3.安装词法解析器4.修改源码5. 编译、创建配置文件6.修改配置文件boa.conf7.运行测试1.下载boa Boa Webserver http://www.boa.org/ 2.解压 tar -xf boa-0.94.13.tar.gz3.安装词法解析器 sudo apt-get install bisonsud…

我们为什么不能忽视业务只讲数据治理?_光点科技

数据治理是一项重要的业务实践&#xff0c;可以帮助组织更好地管理和利用数据。然而&#xff0c;一些企业错误地将数据治理视为一项独立的技术实践&#xff0c;而忽略了业务需求。那么&#xff0c;为什么不能忽视业务&#xff0c;只讲数据治理呢&#xff1f;首先&#xff0c;数…

网络基础知识和常用命令

IP、子网掩码、网关、DNS、端口号网络的基本概念客户端:应用 C/S&#xff08;客户端/服务器&#xff09; B/S&#xff08;浏览器/服务器&#xff09;服务器&#xff1a;为客户端提供服务、数据、资源的机器请求&#xff1a;客户端向服务器索取数据响应&#xff1a;服务器对客户…

H2数据库

H2是一个用Java开发的嵌入式数据库&#xff0c;它本身只是一个类库&#xff0c;可以直接嵌入到应用项目中。 H2简介 H2是一个Java编写的关系型数据库&#xff0c;它可以被嵌入Java应用程序中使用&#xff0c;或者作为一个单独的数据库服务器运行。 H2数据库的前身是 Hypersoni…

线段树SegmentTree

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349;&#x1f95d; 什么是线段树&#xff0c;它能解决什么样的问题&#xff1f; 文章目录&#x1f36d;问题引入&#x1f95d;线段…