├── README.md ├── arm ├── CarPlayMainWW │ ├── Assets │ │ ├── Icon │ │ │ ├── bluetooth.png │ │ │ ├── car.png │ │ │ ├── map.png │ │ │ ├── music.png │ │ │ ├── phonecall.png │ │ │ ├── settings.png │ │ │ └── weather.png │ │ └── Image │ │ │ └── background-main.jpg │ ├── CarPlayMainWW.pro │ ├── CarPlayMainWW.pro.user │ ├── CarPlayMainWW.pro.user.15a0740 │ ├── CarPlayMainWW.pro.user.1bd63b1 │ ├── MainWidget.cpp │ ├── MainWidget.h │ ├── MainWidget.ui │ ├── MyQSS.h │ ├── Resources.qrc │ ├── Threads.cpp │ ├── Threads.h │ ├── main.cpp │ ├── sr04.cpp │ └── sr04.h ├── Drivers │ └── sr04 │ │ ├── Makefile │ │ └── gpio_drv.c └── MusicPlayerWW │ ├── Assets │ ├── Icon │ │ ├── down-arrow.png │ │ ├── duration.png │ │ ├── folder.png │ │ ├── list.png │ │ ├── loop.png │ │ ├── loop41.png │ │ ├── music-playing.png │ │ ├── music.png │ │ ├── mute.png │ │ ├── neaten.png │ │ ├── next.png │ │ ├── non-music.png │ │ ├── pause.png │ │ ├── playing.png │ │ ├── pre.png │ │ ├── quit.png │ │ ├── random.png │ │ ├── singer.png │ │ ├── sliderHandle.png │ │ ├── sweep.png │ │ ├── title.png │ │ ├── up-arrow.png │ │ └── vol.png │ └── Image │ │ └── Background1.jpg │ ├── LyricWidget.cpp │ ├── LyricWidget.h │ ├── LyricWidget.ui │ ├── MainWidget.cpp │ ├── MainWidget.h │ ├── MainWidget.ui │ ├── MusicList.cpp │ ├── MusicList.h │ ├── MusicListWidget.cpp │ ├── MusicListWidget.h │ ├── MusicPlayerWW.pro │ ├── MusicPlayerWW.pro.user │ ├── MyMusic.cpp │ ├── MyMusic.h │ ├── MyQSS.h │ ├── Resources.qrc │ └── main.cpp └── x86 ├── CarPlayMainWW ├── Assets │ ├── Icon │ │ ├── bluetooth.png │ │ ├── car.png │ │ ├── map.png │ │ ├── music.png │ │ ├── phonecall.png │ │ ├── settings.png │ │ └── weather.png │ └── Image │ │ └── background-main.jpg ├── CarPlayMainWW.pro ├── CarPlayMainWW.pro.user ├── CarPlayMainWW.pro.user.15a0740 ├── CarPlayMainWW.pro.user.1bd63b1 ├── MainWidget.cpp ├── MainWidget.h ├── MainWidget.ui ├── MyQSS.h ├── Resources.qrc ├── Threads.cpp ├── Threads.h └── main.cpp └── MusicPlayerWW ├── Assets ├── Icon │ ├── down-arrow.png │ ├── duration.png │ ├── folder.png │ ├── list.png │ ├── loop.png │ ├── loop41.png │ ├── music-playing.png │ ├── music.png │ ├── mute.png │ ├── neaten.png │ ├── next.png │ ├── non-music.png │ ├── pause.png │ ├── playing.png │ ├── pre.png │ ├── quit.png │ ├── random.png │ ├── singer.png │ ├── sliderHandle.png │ ├── sweep.png │ ├── title.png │ ├── up-arrow.png │ └── vol.png └── Image │ └── Background1.jpg ├── LyricWidget.cpp ├── LyricWidget.h ├── LyricWidget.ui ├── MainWidget.cpp ├── MainWidget.h ├── MainWidget.ui ├── MusicList.cpp ├── MusicList.h ├── MusicListWidget.cpp ├── MusicListWidget.h ├── MusicPlayerWW.pro ├── MusicPlayerWW.pro.user ├── MyMusic.cpp ├── MyMusic.h ├── MyQSS.h ├── Resources.qrc └── main.cpp /README.md: -------------------------------------------------------------------------------- 1 | # 基于Qt5的车载中控 2 | 3 | 基于Qt5的车载中控,最终目标是放入Linux开发板中使用触摸屏控制。 4 | 5 | 通过此项目巩固自己学习的知识,包括C/C++,Qt (QSS, SQLite),进程通信、多线程,Linux驱动编写。 6 | 7 | *音乐播放器的轮子主要参考:* 8 | 9 | *代码思路也有参考:* 10 | 11 | ## 功能 12 | 13 | ### 车载主菜单 (待完善) 14 | 15 | ![20230803221835](https://image-hosting-1313474851.cos.ap-shanghai.myqcloud.com/Notes/20230803221835.png) 16 | 17 | - 当前日期时间实时显示。 18 | - 创建**共享内存**,实现主界面进程与播放器进程间通信。 19 | - (arm版) 编写传感器的**驱动程序**,并采集数据。 20 | 21 | ### 音乐播放器 22 | 23 | ![20230803223119](https://image-hosting-1313474851.cos.ap-shanghai.myqcloud.com/Notes/20230803223119.png)|![20230803223203](https://image-hosting-1313474851.cos.ap-shanghai.myqcloud.com/Notes/20230803223203.png) 24 | ---|--- 25 | ![20230803223237](https://image-hosting-1313474851.cos.ap-shanghai.myqcloud.com/Notes/20230803223237.png)|![20230803223417](https://image-hosting-1313474851.cos.ap-shanghai.myqcloud.com/Notes/20230803223417.png) 26 | 27 | - 使用文件操作打开音乐文件 (这里其实不妥,本地音乐最好还是一开始就扫描好所有歌曲并加载到列表中,这样才是适合车载触摸屏的操作,后续需修改),支持.mp3,.flac文件。 28 | - 解析歌词文件并随播放进度显示。 29 | - Music类解析歌曲信息 (要是改成自动扫描本地歌曲的模式,歌曲信息就解析不出来,可能是信息太大push_back到类对象需要很久时间,其实现在很多车载中控都是联网的,自带各种网络音乐应用,本地音乐就不需要解析歌曲信息这一功能了,后续再修改)。 30 | - 歌曲列表支持整理、排序、清空,并使用Qt内置的SQLite储存目前的歌曲列表。 31 | - 播放按钮有两个QSS样式,即正在播放和暂停状态,使用信号与槽机制实现播放状态变化按钮跟随更新,滑槽与播放进度、播放时间同样使用信号与槽机制控制。 32 | - 右上角退出按钮按下后回到主菜单界面,播放器隐藏且能继续播放。 33 | 34 | ## 设备及环境 35 | 36 | - 硬件:imx6ull_pro,LCD屏幕,SR04超声波测距模块 37 | - 开发软件:Qt 5.12.8 38 | - 操作系统:Windows、Linux 39 | - 交叉编译工具链:arm-buildroot-linux-gnueabihf-gcc v7.5.0 40 | - 板子上的触摸库:tslib (记得设置tslib和Qt的环境变量) 41 | 42 | ## 使用 43 | 44 | 1. QtCreator打开CarPlayMainWW和MusicPlayerWW的工程文件,可执行文件由qmake执行后生成。 45 | 2. 运行CarPlayMainWW可执行文件。 46 | 47 | ## 改进日志 48 | 49 | - TODO 50 | - [ ] 读取音乐文件的方式改为本地指定路径自动读取,取消数据库增删、解析歌曲信息功能 51 | - [ ] 更多的应用...... 52 | 53 | - V1.1 54 | - [x] 使用多线程 55 | - [x] 添加arm版本,编写SR04超声波测距模块驱动程序,在Qt上编写传感器数据采集的线程 56 | - [x] Menu初始化时MusicPlayer一起启动,且MusicPlayer第一次启动时隐藏界面 57 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/bluetooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/bluetooth.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/car.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/map.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/music.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/phonecall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/phonecall.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/settings.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Icon/weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Icon/weather.png -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Assets/Image/background-main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/CarPlayMainWW/Assets/Image/background-main.jpg -------------------------------------------------------------------------------- /arm/CarPlayMainWW/CarPlayMainWW.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 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 | Threads.cpp \ 20 | main.cpp \ 21 | MainWidget.cpp \ 22 | sr04.cpp 23 | 24 | HEADERS += \ 25 | MainWidget.h \ 26 | MyQSS.h \ 27 | Threads.h \ 28 | sr04.h 29 | 30 | FORMS += \ 31 | MainWidget.ui 32 | 33 | # Default rules for deployment. 34 | qnx: target.path = /tmp/$${TARGET}/bin 35 | else: unix:!android: target.path = /opt/$${TARGET}/bin 36 | !isEmpty(target.path): INSTALLS += target 37 | 38 | RESOURCES += \ 39 | Resources.qrc 40 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/MainWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | #include "ui_MainWidget.h" 3 | #include 4 | #include "MyQSS.h" 5 | #include 6 | #include 7 | #include 8 | 9 | //QString musicAppPath = "D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/build-MusicPlayerWW-Desktop_Qt_5_12_8_MinGW_64_bit-Debug/debug/MusicPlayerWW.exe"; // 音乐播放器app的位置 10 | QString musicAppPath = "./MusicPlayerWW"; 11 | int preNum = 0; 12 | 13 | MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget) 14 | { 15 | ui->setupUi(this); 16 | 17 | init_ui(); 18 | init_dev(); 19 | proMusic = new QProcess(); 20 | init_shm(); 21 | } 22 | 23 | MainWidget::~MainWidget() 24 | { 25 | qDebug() << "关闭"; 26 | proMusic->close(); 27 | delete ui; 28 | } 29 | 30 | void MainWidget::init_ui() 31 | { 32 | setFixedSize(1024, 600); 33 | setAttribute(Qt::WA_StyledBackground); // 告诉窗口,应该使用设置的样式表进行渲染控件背景 34 | setWindowFlags(Qt::FramelessWindowHint); // 去除标题栏 35 | ui->dateLabel->setStyleSheet(DateLabelStyle()); // 设置日期标签的QSS 36 | ui->timeLabel->setStyleSheet(TimeLabelStyle()); // 设置时间标签的QSS 37 | 38 | // 线程:获取当前日期时间并显示 39 | // timerDT = new QTimer(this); 40 | // timerDT->start(500); 41 | timeShowThread = new Thread_DateTime(ui->dateLabel, ui->timeLabel); 42 | timeShowThread->start(); 43 | connect(timeShowThread, &QThread::finished, timeShowThread, &QObject::deleteLater); 44 | } 45 | 46 | void MainWidget::init_dev() 47 | { 48 | sr04Thread = new Thread_sr04; 49 | sr04Thread->start(); 50 | connect(sr04Thread, &QThread::finished, sr04Thread, &QObject::deleteLater); 51 | // switchAppTimer = new QTimer(this); 52 | // switchAppTimer->start(200); 53 | // connect(switchAppTimer, &QTimer::timeout, [&](){}); 54 | } 55 | 56 | void MainWidget::shm_to_zero() 57 | { 58 | // 开始写 59 | shmMusicToMenu.lock(); 60 | int* sharedData = static_cast(shmMusicToMenu.data()); 61 | *sharedData = 0; 62 | shmMusicToMenu.unlock(); 63 | shmMusicToMenu.detach(); 64 | } 65 | 66 | void MainWidget::exec_by_num(int num) 67 | { 68 | switch(num) 69 | { 70 | case SWITCH_VISIBILITY: 71 | this->show(); 72 | shm_to_zero(); 73 | break; 74 | default: 75 | break; 76 | } 77 | } 78 | 79 | void MainWidget::init_shm() 80 | { 81 | shmMenuToMusic.setKey(QStringLiteral("shm_for_music")); // 音乐播放器用共享内存键值 82 | shmMusicToMenu.setKey(QStringLiteral("shm_for_menu")); // 菜单用共享内存键值 83 | detachTimer = new QTimer(this); 84 | // 定时监测共享内存并读数据 85 | readTimer = new QTimer(this); 86 | connect(readTimer, &QTimer::timeout, [&]() 87 | { 88 | // qDebug() << "等待"; 89 | if(shmMusicToMenu.attach()) 90 | { 91 | int* sharedData = static_cast(shmMusicToMenu.data()); 92 | int currentNum = *sharedData; 93 | qDebug() << "Menu:" << currentNum; 94 | exec_by_num(currentNum); 95 | shmMusicToMenu.detach(); 96 | } 97 | }); 98 | readTimer->start(1000); 99 | } 100 | 101 | void MainWidget::zoom(QPushButton* btn) 102 | { 103 | QPropertyAnimation* animation = new QPropertyAnimation(btn,"geometry"); 104 | //设置动画时间间隔 105 | animation->setDuration(400); 106 | //设置起始位置 107 | animation->setStartValue(QRect(btn->x(), btn->y()+4, btn->width(), btn->height())); 108 | //设置结束位置 109 | animation->setEndValue(QRect(btn->x(), btn->y(), btn->width(), btn->height())); 110 | //设置弹跳曲线 111 | animation->setEasingCurve(QEasingCurve::OutBounce); 112 | //执行动画 113 | animation->start(); 114 | } 115 | 116 | void MainWidget::on_btnQuit_clicked() 117 | { 118 | zoom(ui->btnQuit); 119 | // sr04_close(); 120 | timeShowThread->stop(); 121 | sr04Thread->stop(); 122 | close(); 123 | } 124 | 125 | void MainWidget::on_btnMusic_clicked() 126 | { 127 | zoom(ui->btnMusic); 128 | // 判断音乐播放器有没有在运行 129 | if(proMusic->state() == QProcess::Running) // 如果音乐进程在 130 | { 131 | if(shmMenuToMusic.isAttached()) shmMenuToMusic.detach(); // 如果之前绑定有,解绑并创建新的 132 | if(!shmMenuToMusic.create(sizeof(int), QSharedMemory::ReadWrite)) 133 | { 134 | qDebug() << "Failed to create shared memory!"; 135 | return; 136 | } 137 | // 开始写 138 | shmMenuToMusic.lock(); 139 | int* sharedData = static_cast(shmMenuToMusic.data()); 140 | *sharedData = SWITCH_VISIBILITY; 141 | shmMenuToMusic.unlock(); 142 | 143 | // 延时detach内存 144 | detachTimer->start(1000); 145 | connect(detachTimer, &QTimer::timeout, [&]() 146 | { 147 | detachTimer->stop(); 148 | shmMenuToMusic.detach(); 149 | }); 150 | 151 | // 延时隐藏进程 152 | this->hide(); 153 | } 154 | else if(proMusic->state() == QProcess::NotRunning) // 如果没开过音乐进程 155 | { 156 | proMusic->close(); 157 | proMusic->start(musicAppPath); 158 | // 延时隐藏进程 159 | this->hide(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/MainWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Threads.h" 11 | 12 | // 共享内存传输的一些命令 13 | #define SWITCH_VISIBILITY 1 // 改变窗体可见性 14 | #define VOL_DOWN 2 // 音量减小 15 | #define VOL_UP 3 // 音量增大 16 | #define MUSIC_PLAY 4 // 播放音乐 17 | #define MUSIC_PAUSE 5 // 暂停音乐 18 | #define CLOSE_APP 6 // 关闭APP 19 | 20 | QT_BEGIN_NAMESPACE 21 | namespace Ui { class MainWidget; } 22 | QT_END_NAMESPACE 23 | 24 | class MainWidget : public QWidget 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | MainWidget(QWidget *parent = nullptr); 30 | ~MainWidget(); 31 | 32 | void init_ui(); 33 | void init_dev(); 34 | void init_shm(); 35 | void exec_by_num(int num); 36 | void shm_to_zero(); 37 | 38 | void zoom(QPushButton* btn); // 按钮动画 39 | 40 | private slots: 41 | void on_btnQuit_clicked(); 42 | 43 | void on_btnMusic_clicked(); 44 | 45 | private: 46 | Ui::MainWidget *ui; 47 | QTimer* timerDT, * switchAppTimer, * readTimer, * detachTimer; 48 | QProcess* proMusic; 49 | Thread_DateTime* timeShowThread; 50 | Thread_sr04* sr04Thread; 51 | QSharedMemory shmMenuToMusic, shmMusicToMenu; 52 | }; 53 | 54 | #endif // MAINWIDGET_H 55 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/MainWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 600 11 | 12 | 13 | 14 | MainWidget 15 | 16 | 17 | #MainWidget 18 | { 19 | background-image: url(:/Assets/Image/background-main.jpg); 20 | } 21 | 22 | 23 | 24 | 25 | 730 26 | 40 27 | 220 28 | 140 29 | 30 | 31 | 32 | false 33 | 34 | 35 | #dateTimeGroupBox 36 | { 37 | background:transparent; 38 | border:transparent; 39 | } 40 | 41 | 42 | 43 | 44 | 45 | Qt::AlignCenter 46 | 47 | 48 | false 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 0 57 | 0 58 | 220 59 | 60 60 | 61 | 62 | 63 | 64 | 65 | 66 | Qt::AlignCenter 67 | 68 | 69 | 70 | 71 | 72 | 0 73 | 60 74 | 220 75 | 80 76 | 77 | 78 | 79 | 80 | 81 | 82 | Qt::AlignCenter 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 520 91 | 1024 92 | 80 93 | 94 | 95 | 96 | #frame 97 | { 98 | background:rgb(248,248,255); 99 | border-top-style:solid; 100 | border-top-width:1px; 101 | border-top-color:rgba(220, 220, 220, 1); 102 | } 103 | 104 | 105 | QFrame::StyledPanel 106 | 107 | 108 | QFrame::Raised 109 | 110 | 111 | 112 | 113 | 0 114 | 0 115 | 220 116 | 80 117 | 118 | 119 | 120 | 121 | 15 122 | 75 123 | true 124 | 125 | 126 | 127 | #btnMusic 128 | { 129 | border-style:solid; 130 | border-color:rgb(0, 0, 0); 131 | border-right-width:2px; 132 | background:rgba(248, 248, 255, 0); 133 | } 134 | 135 | 136 | 本地音乐 137 | 138 | 139 | 140 | :/Assets/Icon/music.png:/Assets/Icon/music.png 141 | 142 | 143 | 144 | 30 145 | 30 146 | 147 | 148 | 149 | false 150 | 151 | 152 | false 153 | 154 | 155 | false 156 | 157 | 158 | false 159 | 160 | 161 | false 162 | 163 | 164 | 165 | 166 | 167 | 220 168 | 0 169 | 220 170 | 80 171 | 172 | 173 | 174 | 175 | 15 176 | 75 177 | true 178 | 179 | 180 | 181 | #btnIdle1 182 | { 183 | border-style:solid; 184 | border-color:rgb(0, 0, 0); 185 | border-left-width:2px; 186 | border-right-width:2px; 187 | background:rgba(248, 248, 255, 0); 188 | } 189 | 190 | 191 | 192 | 193 | 194 | 195 | 30 196 | 30 197 | 198 | 199 | 200 | false 201 | 202 | 203 | false 204 | 205 | 206 | false 207 | 208 | 209 | false 210 | 211 | 212 | false 213 | 214 | 215 | 216 | 217 | 218 | 440 219 | 0 220 | 220 221 | 80 222 | 223 | 224 | 225 | 226 | 15 227 | 75 228 | true 229 | 230 | 231 | 232 | #btnIdle2 233 | { 234 | border-style:solid; 235 | border-color:rgb(0, 0, 0); 236 | border-left-width:2px; 237 | border-right-width:2px; 238 | background:rgba(248, 248, 255, 0); 239 | } 240 | 241 | 242 | 243 | 244 | 245 | 246 | 30 247 | 30 248 | 249 | 250 | 251 | false 252 | 253 | 254 | false 255 | 256 | 257 | false 258 | 259 | 260 | false 261 | 262 | 263 | false 264 | 265 | 266 | 267 | 268 | 269 | 660 270 | 0 271 | 220 272 | 80 273 | 274 | 275 | 276 | 277 | 15 278 | 75 279 | true 280 | 281 | 282 | 283 | #btnIdle3 284 | { 285 | border-style:solid; 286 | border-color:rgb(0, 0, 0); 287 | border-left-width:2px; 288 | border-right-width:2px; 289 | background:rgba(248, 248, 255, 0); 290 | } 291 | 292 | 293 | 294 | 295 | 296 | 297 | 30 298 | 30 299 | 300 | 301 | 302 | false 303 | 304 | 305 | false 306 | 307 | 308 | false 309 | 310 | 311 | false 312 | 313 | 314 | false 315 | 316 | 317 | 318 | 319 | 320 | 880 321 | 0 322 | 144 323 | 80 324 | 325 | 326 | 327 | 328 | 15 329 | 75 330 | true 331 | 332 | 333 | 334 | #btnQuit 335 | { 336 | border-style:solid; 337 | border-left-width:2px; 338 | border-color:rgb(0, 0, 0); 339 | background:rgba(248, 248, 255, 0); 340 | } 341 | 342 | 343 | 退出 344 | 345 | 346 | 347 | 30 348 | 30 349 | 350 | 351 | 352 | false 353 | 354 | 355 | false 356 | 357 | 358 | false 359 | 360 | 361 | false 362 | 363 | 364 | false 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/MyQSS.h: -------------------------------------------------------------------------------- 1 | #ifndef MYQSS_H 2 | #define MYQSS_H 3 | #include 4 | 5 | // 此文件统一存放一些QSS样式代码 6 | 7 | // 日期标签字体 8 | inline QString DateLabelStyle() 9 | { 10 | return 11 | "#dateLabel" 12 | "{" 13 | "font-size:30px;" 14 | "font-weight:bold;" 15 | "background:transparent;" 16 | "}"; 17 | } 18 | 19 | // 时间标签字体 20 | inline QString TimeLabelStyle() 21 | { 22 | return 23 | "#timeLabel" 24 | "{" 25 | "font-size:50px;" 26 | "font-weight:bold;" 27 | "background:transparent;" 28 | "}"; 29 | } 30 | 31 | #endif // MYQSS_H 32 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assets/Image/background-main.jpg 4 | Assets/Icon/bluetooth.png 5 | Assets/Icon/car.png 6 | Assets/Icon/map.png 7 | Assets/Icon/music.png 8 | Assets/Icon/phonecall.png 9 | Assets/Icon/settings.png 10 | Assets/Icon/weather.png 11 | 12 | 13 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Threads.cpp: -------------------------------------------------------------------------------- 1 | #include "Threads.h" 2 | #include 3 | #include 4 | #include 5 | // 为传感器添加的 6 | #include "sr04.h" 7 | #include 8 | #include 9 | #include 10 | int sr04Val; 11 | struct pollfd fds[1]; 12 | #define CMD_TRIG 100 13 | #define TIMEOUT_MS 5000 14 | 15 | void Thread_DateTime::run() 16 | { 17 | while(1) 18 | { 19 | mutex.lock(); 20 | if(!threadExist) break; 21 | mutex.unlock(); 22 | 23 | // 获取当前日期 24 | QDate currentDate = QDate::currentDate(); 25 | dateL->setText(QStringLiteral("%1-%2-%3").arg(currentDate.year()).arg(currentDate.month()).arg(currentDate.day())); 26 | // 获取当前时间 27 | QTime currentTime = QTime::currentTime(); 28 | timeL->setText(currentTime.toString()); 29 | usleep(500000); // 500ms 30 | } 31 | mutex.unlock(); 32 | } 33 | void Thread_DateTime::stop() 34 | { 35 | mutex.lock(); 36 | threadExist = false; 37 | mutex.unlock(); 38 | } 39 | 40 | void Thread_sr04::run() 41 | { 42 | if(sr04_open("/dev/sr04") == -1) return; 43 | while(1) 44 | { 45 | mutex.lock(); 46 | if(!threadExist) break; 47 | mutex.unlock(); 48 | 49 | sr04_ioctl(CMD_TRIG, 0); 50 | fds[0].fd = sr04_fd; 51 | fds[0].events = POLLIN; 52 | if(sr04_poll(fds, 1, TIMEOUT_MS) == 1) 53 | { 54 | if(sr04_read(&sr04Val, 4) == 4) 55 | { 56 | if((sr04Val * 17 / 1000000) > 227) qDebug() << sr04Val * 17 / 1000000 << "cm, Far\n"; 57 | else if((sr04Val * 17 / 1000000) > 20 && ((sr04Val * 17 / 1000000) <= 227)) qDebug() << sr04Val * 17 / 1000000 << "cm, Well\n"; 58 | else qDebug() << sr04Val * 17 / 1000000 << "cm, Careful!\n"; 59 | } 60 | } 61 | else qDebug() << "poll timeout/error\n"; 62 | usleep(200000); // 200ms 63 | } 64 | } 65 | void Thread_sr04::stop() 66 | { 67 | mutex.lock(); 68 | threadExist = false; 69 | mutex.unlock(); 70 | } 71 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/Threads.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_H 2 | #define THREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // 线程:日期时间显示 11 | class Thread_DateTime : public QThread 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit Thread_DateTime(QLabel* d, QLabel* t) : dateL(d), timeL(t), threadExist(true) {} 16 | void stop(); 17 | protected: 18 | void run(); 19 | private: 20 | QLabel* dateL, * timeL; 21 | QMutex mutex; 22 | bool threadExist; 23 | }; 24 | 25 | // 线程:sr04超声波测距模块 26 | class Thread_sr04 : public QThread 27 | { 28 | Q_OBJECT 29 | public: 30 | explicit Thread_sr04() : threadExist(true) {} 31 | void stop(); 32 | protected: 33 | void run(); 34 | private: 35 | QMutex mutex; 36 | bool threadExist; 37 | }; 38 | 39 | #endif // THREAD_H 40 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWidget w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/sr04.cpp: -------------------------------------------------------------------------------- 1 | #include "sr04.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int sr04_fd = 0; 11 | 12 | int sr04_open(const char* devname) 13 | { 14 | sr04_fd = open(devname, O_RDWR); 15 | if (sr04_fd == -1) 16 | { 17 | printf("can not open file %s\n", devname); 18 | return -1; 19 | } 20 | return 0; 21 | } 22 | int sr04_ioctl(unsigned int cmd, unsigned long arg) 23 | { 24 | ioctl(sr04_fd, cmd, arg); 25 | return 0; 26 | } 27 | int sr04_poll(struct pollfd* fds, int nfds, int timeout) 28 | { 29 | return poll(fds, nfds, timeout); 30 | } 31 | int sr04_read(void* buf, int nBytes) 32 | { 33 | return read(sr04_fd, buf, nBytes); 34 | } 35 | void sr04_close(void) 36 | { 37 | if(sr04_fd) close(sr04_fd); 38 | } 39 | -------------------------------------------------------------------------------- /arm/CarPlayMainWW/sr04.h: -------------------------------------------------------------------------------- 1 | #ifndef SR04_H 2 | #define SR04_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" 6 | { 7 | #endif 8 | 9 | extern int sr04_open(const char* devname); 10 | extern int sr04_ioctl(unsigned int cmd, unsigned long arg); 11 | extern int sr04_read(void* buf, int nBytes); 12 | extern void sr04_close(void); 13 | extern int sr04_poll(struct pollfd* fds, int nfds, int timeout); 14 | extern int sr04_fd; 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif // SR04_H 21 | -------------------------------------------------------------------------------- /arm/Drivers/sr04/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR 3 | # 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: 4 | # 2.1 ARCH, 比如: export ARCH=arm64 5 | # 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- 6 | # 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 7 | # 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, 8 | # 请参考各开发板的高级用户使用手册 9 | 10 | KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录 11 | 12 | all: 13 | make -C $(KERN_DIR) M=`pwd` modules 14 | clean: 15 | make -C $(KERN_DIR) M=`pwd` modules clean 16 | 17 | # 参考内核源码drivers/char/ipmi/Makefile 18 | # 要想把a.c, b.c编译成ab.ko, 可以这样指定: 19 | # ab-y := a.o b.o 20 | # obj-m += ab.o 21 | 22 | obj-m += gpio_drv.o 23 | 24 | -------------------------------------------------------------------------------- /arm/Drivers/sr04/gpio_drv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define CMD_TRIG 100 29 | 30 | struct gpio_desc{ 31 | int gpio; 32 | int irq; 33 | char *name; 34 | int key; 35 | struct timer_list key_timer; 36 | } ; 37 | 38 | static struct gpio_desc gpios[2] = { 39 | {115, 0, "trig", }, 40 | {116, 0, "echo", }, 41 | }; 42 | 43 | /* 主设备号 */ 44 | static int major = 0; 45 | static struct class *gpio_class; 46 | 47 | /* 环形缓冲区 */ 48 | #define BUF_LEN 128 49 | static int g_keys[BUF_LEN]; 50 | static int r, w; 51 | 52 | struct fasync_struct *button_fasync; 53 | 54 | #define NEXT_POS(x) ((x+1) % BUF_LEN) 55 | 56 | static int is_key_buf_empty(void) 57 | { 58 | return (r == w); 59 | } 60 | 61 | static int is_key_buf_full(void) 62 | { 63 | return (r == NEXT_POS(w)); 64 | } 65 | 66 | static void put_key(int key) 67 | { 68 | if (!is_key_buf_full()) 69 | { 70 | g_keys[w] = key; 71 | w = NEXT_POS(w); 72 | } 73 | } 74 | 75 | static int get_key(void) 76 | { 77 | int key = 0; 78 | if (!is_key_buf_empty()) 79 | { 80 | key = g_keys[r]; 81 | r = NEXT_POS(r); 82 | } 83 | return key; 84 | } 85 | 86 | 87 | static DECLARE_WAIT_QUEUE_HEAD(gpio_wait); 88 | 89 | /* 实现对应的open/read/write等函数,填入file_operations结构体 */ 90 | static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *offset) 91 | { 92 | //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 93 | int err; 94 | int key; 95 | 96 | if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK)) 97 | return -EAGAIN; 98 | 99 | wait_event_interruptible(gpio_wait, !is_key_buf_empty()); 100 | key = get_key(); 101 | if(key == -1) return -ENODATA; 102 | err = copy_to_user(buf, &key, 4); 103 | 104 | return 4; 105 | } 106 | static unsigned int sr04_poll(struct file *fp, poll_table * wait) 107 | { 108 | //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 109 | poll_wait(fp, &gpio_wait, wait); 110 | return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM; 111 | } 112 | static int sr04_fasync(int fd, struct file *file, int on) 113 | { 114 | if (fasync_helper(fd, file, on, &button_fasync) >= 0) 115 | return 0; 116 | else 117 | return -EIO; 118 | } 119 | // ioctl(fd, CMD, ARG) 120 | static long sr04_ioctl(struct file *filp, unsigned int command, unsigned long arg) 121 | { 122 | // send trig 123 | switch(command) 124 | { 125 | case CMD_TRIG: 126 | gpio_set_value(gpios[0].gpio, 1); 127 | udelay(20); // delay 20μs 128 | gpio_set_value(gpios[0].gpio, 0); 129 | break; 130 | 131 | // start timer 132 | mod_timer(&gpios[1].key_timer, jiffies + msecs_to_jiffies(50)); // 再加一段时间,因为sr04最长测量450cm,所需时间约为26ms 133 | } 134 | 135 | return 0; 136 | } 137 | 138 | 139 | /* 定义自己的file_operations结构体 */ 140 | static struct file_operations sr04_drv = { 141 | .owner = THIS_MODULE, 142 | .read = sr04_read, 143 | .poll = sr04_poll, 144 | .fasync = sr04_fasync, 145 | .unlocked_ioctl = sr04_ioctl, 146 | }; 147 | 148 | 149 | static irqreturn_t sr04_isr(int irq, void *dev_id) 150 | { 151 | int val; 152 | struct gpio_desc *gpio_desc = dev_id; 153 | static u64 rising_time = 0; 154 | u64 timeErr; 155 | val = gpio_get_value(gpio_desc->gpio); 156 | 157 | if(val) // 上升沿 158 | { 159 | // 上升沿记录起始时间 160 | rising_time = ktime_get_ns(); 161 | } 162 | else 163 | { 164 | 165 | if(rising_time == 0) // 错过上升沿时 166 | { 167 | // printk("Missing rising interrupt\n"); 168 | return IRQ_HANDLED; 169 | } 170 | 171 | // stop timer 172 | del_timer(&gpios[1].key_timer); 173 | 174 | // 下降沿记录结束时间,并计算时间差,计算距离 175 | timeErr = ktime_get_ns() - rising_time; 176 | rising_time = 0; 177 | put_key(timeErr); 178 | 179 | wake_up_interruptible(&gpio_wait); 180 | kill_fasync(&button_fasync, SIGIO, POLL_IN); 181 | // printk("sr04_isr key %d irq happened\n", gpio_desc->gpio); 182 | } 183 | return IRQ_HANDLED; 184 | } 185 | 186 | static void sr04_timer_expire(unsigned long data) 187 | { 188 | put_key(-1); 189 | 190 | wake_up_interruptible(&gpio_wait); 191 | kill_fasync(&button_fasync, SIGIO, POLL_IN); 192 | 193 | } 194 | 195 | /* 在入口函数 */ 196 | static int __init sr04_init(void) 197 | { 198 | int err; 199 | 200 | printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 201 | 202 | // trig pin 203 | err = gpio_request(gpios[0].gpio, gpios[0].name); 204 | gpio_direction_output(gpios[0].gpio, 0); // Trig平时是低电平 205 | // echo pin 206 | gpios[1].irq = gpio_to_irq(gpios[1].gpio); 207 | err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]); // 申请中断 208 | 209 | // init timer 210 | setup_timer(&gpios[1].key_timer, sr04_timer_expire, (unsigned long)&gpios[1]); 211 | 212 | /* 注册file_operations */ 213 | major = register_chrdev(0, "100ask_sr04", &sr04_drv); 214 | 215 | gpio_class = class_create(THIS_MODULE, "100ask_sr04_class"); 216 | if (IS_ERR(gpio_class)) { 217 | printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 218 | unregister_chrdev(major, "100ask_sr04"); 219 | return PTR_ERR(gpio_class); 220 | } 221 | 222 | device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */ 223 | 224 | return err; 225 | } 226 | 227 | /* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 228 | */ 229 | static void __exit sr04_exit(void) 230 | { 231 | printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 232 | 233 | device_destroy(gpio_class, MKDEV(major, 0)); 234 | class_destroy(gpio_class); 235 | unregister_chrdev(major, "100ask_sr04"); 236 | 237 | // trig pin 238 | gpio_free(gpios[0].gpio); 239 | // echo pin 240 | free_irq(gpios[1].irq, &gpios[1]); 241 | 242 | // delete timer 243 | del_timer(&gpios[1].key_timer); 244 | } 245 | 246 | 247 | /* 7. 其他完善:提供设备信息,自动创建设备节点 */ 248 | 249 | module_init(sr04_init); 250 | module_exit(sr04_exit); 251 | 252 | MODULE_LICENSE("GPL"); 253 | 254 | 255 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/down-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/down-arrow.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/duration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/duration.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/folder.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/list.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/loop.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/loop41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/loop41.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/music-playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/music-playing.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/music.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/mute.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/neaten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/neaten.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/next.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/non-music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/non-music.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/pause.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/playing.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/pre.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/quit.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/random.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/singer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/singer.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/sliderHandle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/sliderHandle.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/sweep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/sweep.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/title.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/up-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/up-arrow.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Icon/vol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Icon/vol.png -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Assets/Image/Background1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/arm/MusicPlayerWW/Assets/Image/Background1.jpg -------------------------------------------------------------------------------- /arm/MusicPlayerWW/LyricWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "LyricWidget.h" 2 | #include "ui_LyricWidget.h" 3 | 4 | #include 5 | #include 6 | 7 | LyricWidget::LyricWidget(QWidget *parent) : QWidget(parent), ui(new Ui::LyricWidget) 8 | { 9 | ui->setupUi(this); 10 | clear(); // 先清空完 11 | } 12 | 13 | LyricWidget::~LyricWidget() 14 | { 15 | delete ui; 16 | } 17 | 18 | bool operator <(const LyricLine& A, const LyricLine& B) 19 | { 20 | return A.time < B.time; 21 | } 22 | 23 | bool LyricWidget::process(QString filePath) 24 | { 25 | // 打开歌词文件 26 | QFile lyricFile(filePath); 27 | lyricFile.open(QFile::ReadOnly); 28 | QString content(QString::fromLocal8Bit(lyricFile.readAll())); 29 | lyricFile.close(); 30 | lines.clear(); 31 | 32 | const QRegExp rx("\\[(\\d+):(\\d+(\\.\\d+)?)\\]"); // 用来查找时间标签的正则表达式 33 | 34 | // 步骤1 35 | int pos = rx.indexIn(content); 36 | if (pos == -1) { 37 | return false; 38 | } 39 | else { 40 | int lastPos; 41 | QList timeLabels; 42 | do { 43 | // 步骤2 44 | timeLabels << (rx.cap(1).toInt() * 60 + rx.cap(2).toDouble()) * 1000; 45 | lastPos = pos + rx.matchedLength(); 46 | pos = rx.indexIn(content, lastPos); 47 | if (pos == -1) { 48 | QString text = content.mid(lastPos).trimmed(); 49 | foreach (const int& time, timeLabels) 50 | lines.push_back(LyricLine(time, text)); 51 | break; 52 | } 53 | // 步骤3 54 | QString text = content.mid(lastPos, pos - lastPos); 55 | if (!text.isEmpty()) { 56 | foreach (const int& time, timeLabels) 57 | lines.push_back(LyricLine(time, text.trimmed())); 58 | timeLabels.clear(); 59 | } 60 | } 61 | while (true); 62 | // 步骤4 63 | stable_sort(lines.begin(), lines.end()); 64 | } 65 | if (lines.size()) return true; 66 | return false; 67 | } 68 | 69 | int LyricWidget::get_index(qint64 position) 70 | { 71 | if(!lines.size()){ 72 | return -1; 73 | }else{ 74 | if(lines[0].time>=position){ 75 | return 0; 76 | } 77 | } 78 | int i=1; 79 | for(i=1;i=position){ 81 | return i-1; 82 | } 83 | } 84 | return lines.size()-1; 85 | } 86 | 87 | void LyricWidget::show(qint64 position) 88 | { 89 | int index=get_index(position); 90 | if(index==-1){ 91 | ui->lyricP3->setText(""); 92 | ui->lyricP2->setText(""); 93 | ui->lyricP1->setText(""); 94 | ui->lyricCenter->setText(u8"当前歌曲无歌词"); 95 | ui->lyricN1->setText(""); 96 | ui->lyricN2->setText(""); 97 | ui->lyricN3->setText(""); 98 | }else{ 99 | ui->lyricP3->setText(get_lyric_text(index-3)); 100 | ui->lyricP2->setText(get_lyric_text(index-2)); 101 | ui->lyricP1->setText(get_lyric_text(index-1)); 102 | ui->lyricCenter->setText(get_lyric_text(index)); 103 | ui->lyricN1->setText(get_lyric_text(index+1)); 104 | ui->lyricN2->setText(get_lyric_text(index+2)); 105 | ui->lyricN3->setText(get_lyric_text(index+3)); 106 | } 107 | } 108 | 109 | QString LyricWidget::get_lyric_text(int index) 110 | { 111 | if(index<0 || index>=lines.size()){ 112 | return ""; 113 | }else{ 114 | return lines[index].text; 115 | } 116 | } 117 | 118 | void LyricWidget::clear() 119 | { 120 | ui->lyricP3->setText(""); 121 | ui->lyricP2->setText(""); 122 | ui->lyricP1->setText(""); 123 | ui->lyricCenter->setText(""); 124 | ui->lyricN1->setText(""); 125 | ui->lyricN2->setText(""); 126 | ui->lyricN3->setText(""); 127 | } 128 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/LyricWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef LYRICWIDGET_H 2 | #define LYRICWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | // 表示一行歌词 (一个时间点 + 对应的歌词文本) 11 | class LyricLine 12 | { 13 | public: 14 | qint64 time; 15 | QString text; 16 | LyricLine(qint64 time, QString text):time(time), text(text) {} 17 | }; 18 | 19 | // 重载比较 (歌词按时间排序) 20 | bool operator <(const LyricLine& A, const LyricLine& B); 21 | 22 | namespace Ui { 23 | class LyricWidget; 24 | } 25 | 26 | class LyricWidget : public QWidget 27 | { 28 | Q_OBJECT 29 | 30 | private: 31 | vector lines; 32 | public: 33 | explicit LyricWidget(QWidget *parent = nullptr); 34 | ~LyricWidget(); 35 | 36 | // 将歌词文件的内容处理为歌词结构的QList 37 | bool process(QString filePath); 38 | 39 | // 根据时间找到对应位置的歌词 40 | int get_index(qint64 position); 41 | 42 | // 显示当前播放进度的歌词 43 | void show(qint64 position); 44 | 45 | // 根据下标获得歌词内容 46 | QString get_lyric_text(int index); 47 | 48 | // 清空歌词Label 49 | void clear(); 50 | 51 | private: 52 | Ui::LyricWidget *ui; 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/LyricWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LyricWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 385 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 0 20 | 20 21 | 600 22 | 40 23 | 24 | 25 | 26 | 27 | 11 28 | 50 29 | false 30 | 31 | 32 | 33 | color:rgb(245, 245, 245); 34 | 35 | 36 | 37 | 38 | 39 | Qt::AlignCenter 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 70 47 | 600 48 | 40 49 | 50 | 51 | 52 | 53 | 11 54 | 50 55 | false 56 | 57 | 58 | 59 | color:rgb(245, 245, 245); 60 | 61 | 62 | 63 | 64 | 65 | Qt::AlignCenter 66 | 67 | 68 | 69 | 70 | 71 | 0 72 | 120 73 | 600 74 | 40 75 | 76 | 77 | 78 | 79 | 11 80 | 50 81 | false 82 | 83 | 84 | 85 | color:rgb(245, 245, 245); 86 | 87 | 88 | 89 | 90 | 91 | Qt::AlignCenter 92 | 93 | 94 | 95 | 96 | 97 | 0 98 | 165 99 | 600 100 | 60 101 | 102 | 103 | 104 | 105 | 15 106 | 75 107 | true 108 | 109 | 110 | 111 | color:rgb(255, 255, 255); 112 | 113 | 114 | 115 | 116 | 117 | Qt::AlignCenter 118 | 119 | 120 | 121 | 122 | 123 | 0 124 | 230 125 | 600 126 | 40 127 | 128 | 129 | 130 | 131 | 11 132 | 50 133 | false 134 | 135 | 136 | 137 | color:rgb(245, 245, 245); 138 | 139 | 140 | 141 | 142 | 143 | Qt::AlignCenter 144 | 145 | 146 | 147 | 148 | 149 | 0 150 | 280 151 | 600 152 | 40 153 | 154 | 155 | 156 | 157 | 11 158 | 50 159 | false 160 | 161 | 162 | 163 | color:rgb(245, 245, 245); 164 | 165 | 166 | 167 | 168 | 169 | Qt::AlignCenter 170 | 171 | 172 | 173 | 174 | 175 | 0 176 | 330 177 | 600 178 | 40 179 | 180 | 181 | 182 | 183 | 11 184 | 50 185 | false 186 | 187 | 188 | 189 | color:rgb(245, 245, 245); 190 | 191 | 192 | 193 | 194 | 195 | Qt::AlignCenter 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MainWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ui_MainWidget.h" 9 | #include "MusicListWidget.h" 10 | #include 11 | #include "MyQSS.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // 设置全局变量 18 | QStringList nameFile; 19 | QString openDir; 20 | int preNum = 0, volRecord = 50; 21 | 22 | MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget) 23 | { 24 | ui->setupUi(this); 25 | 26 | init_ui(); 27 | init_play(); 28 | init_sqlite(); 29 | init_musiclist_widget(); 30 | init_shm(); 31 | 32 | // scan_music(); 33 | // add_music_to_list(nameFile); 34 | // init_playlist(); 35 | 36 | // QUrl baseUrl("file:///D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/孙燕姿-极美.mp3"); // "file:///"不可少 37 | // //QUrl relativeUrl("孙燕姿-极美.mp3"); 38 | // //qDebug() << "Url:" << baseUrl.resolved(relativeUrl) << "\n"; 39 | // MyMusic msc(baseUrl); // resolved:拼接绝对变量和相对变量 40 | // qDebug() << "Author:" << msc.author 41 | // << "Title:" << msc.title 42 | // << "AlbumTitle:" << msc.albumTitle 43 | // << "Duration:" << msc.duration 44 | // << "BitRate:" << msc.bitRate; 45 | } 46 | 47 | MainWidget::~MainWidget() 48 | { 49 | delete ui; 50 | } 51 | 52 | extern QString my_format_time(qint64 msTime); // 外部链接已定义好的函数 53 | 54 | void MainWidget::init_ui() 55 | { 56 | setFixedSize(1024, 600); 57 | setAttribute(Qt::WA_StyledBackground); // 告诉窗口,应该使用设置的样式表进行渲染控件背景 58 | setWindowFlags(Qt::FramelessWindowHint); // 去除标题栏 59 | ui->sliderVol->hide(); 60 | ui->stackedWidget->setCurrentIndex(0); 61 | ui->musicListWidget->set_icon(QIcon(":/Assets/Icon/music.png")); 62 | // 封面图片 63 | QPixmap albumImg(":/Assets/Icon/non-music.png"); 64 | ui->coverPic->setPixmap(albumImg); 65 | ui->coverPic->setScaledContents(true); 66 | // 需要图标变换的图标初始化 67 | ui->btnPlay->setStyleSheet(PauseStyle()); 68 | } 69 | 70 | void MainWidget::init_play() 71 | { 72 | //播放器初始化 73 | player= new QMediaPlayer(this); 74 | playlist=new QMediaPlaylist; 75 | playlist->setPlaybackMode(QMediaPlaylist::Loop); 76 | player->setPlaylist(playlist); 77 | player->setVolume(volRecord); 78 | // 连接播放有关的信号与槽 79 | connect(player, &QMediaPlayer::stateChanged, this, &MainWidget::update_btn_play); // 播放状态改变 80 | connect(ui->sliderProgress, &QAbstractSlider::valueChanged, this, &MainWidget::set_position); // 滑槽拖动带动播放跳转 81 | connect(player, &QMediaPlayer::positionChanged, this, &MainWidget::update_duration); // 歌曲播放时间变化 82 | connect(player, &QMediaPlayer::durationChanged, this, &MainWidget::update_position); // 滑槽位置随播放进度变化而变化 83 | connect(player, &QMediaPlayer::metaDataAvailableChanged, this, &MainWidget::update_info); // 切歌更换元数据 84 | } 85 | 86 | void MainWidget::init_sqlite() 87 | { 88 | QSqlDatabase database; 89 | if(QSqlDatabase::contains("qt_sql_default_connection")) // 检查是否建立过连接了 90 | { 91 | database = QSqlDatabase::database("qt_sql_default_connection"); 92 | } 93 | else 94 | { 95 | database = QSqlDatabase::addDatabase("QSQLITE"); 96 | database.setDatabaseName("Music.db"); 97 | database.setUserName("WW"); 98 | database.setPassword("123456"); 99 | qDebug() << "数据库建立连接"; // 测试用 100 | if(!database.open()) 101 | { 102 | QMessageBox::critical(this,"无法打开数据库文件:Music.db",database.lastError().databaseText()); 103 | exit(-1); 104 | } 105 | } 106 | // 删除表,测试用 107 | // QSqlQuery querySql; 108 | // QString deleteTable = "drop table MusicInfo"; 109 | // querySql.prepare(deleteTable); 110 | // if(querySql.exec()) qDebug() << "删除表"; 111 | // 检查表是否存在,不存在就创建 112 | QSqlQuery querySql; 113 | querySql.exec(QString("select count(*) from sqlite_master where type='table' and name='%1'").arg("MusicInfo")); 114 | if(querySql.next()) 115 | { 116 | if(querySql.value(0).toInt() == 0) 117 | { 118 | QSqlQuery query; 119 | QString createSql = "create table MusicInfo (url varchar(200), author varchar(50), title varchar(50), duration bigint, albumTitle varchar(50), bitRate int)"; 120 | query.prepare(createSql); 121 | if(query.exec()) qDebug() << "表建立"; // 测试用 122 | } 123 | } 124 | } 125 | 126 | void MainWidget::init_musiclist_widget() 127 | { 128 | ui->musicListWidget->clear(); // 先清空 129 | ui->musicListWidget->setFrameShape(QListWidget::NoFrame); // 去除边框 130 | ui->musicListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 131 | ui->musicListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 132 | // 设置字体 133 | QFont font; 134 | font.setPointSize(14); 135 | font.setBold(true); 136 | ui->musicListWidget->setFont(font); 137 | ui->musicListWidget->setIconSize(QSize(30, 30)); 138 | ui->musicListWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel); // 设置为像素滚动 139 | QScroller::grabGesture(ui->musicListWidget, QScroller::LeftMouseButtonGesture); // 设置鼠标左键拖动 140 | // 从数据库恢复 141 | ui->musicListWidget->musicList.read_from_sql(); 142 | ui->musicListWidget->refresh(); 143 | } 144 | 145 | void MainWidget::shm_to_zero() 146 | { 147 | // 开始写 148 | shmMenuToMusic.lock(); 149 | int* sharedData = static_cast(shmMenuToMusic.data()); 150 | *sharedData = 0; 151 | shmMenuToMusic.unlock(); 152 | shmMenuToMusic.detach(); 153 | } 154 | 155 | void MainWidget::exec_by_num(int num) 156 | { 157 | switch(num) 158 | { 159 | case SWITCH_VISIBILITY: 160 | this->show(); 161 | shm_to_zero(); 162 | break; 163 | case VOL_UP: 164 | ui->sliderVol->setValue(volRecord); 165 | shm_to_zero(); 166 | break; 167 | case VOL_DOWN: 168 | if(ui->sliderVol->value() > 20) ui->sliderVol->setValue(20); 169 | volRecord = ui->sliderVol->value(); 170 | shm_to_zero(); 171 | break; 172 | case MUSIC_PLAY: 173 | player->play(); 174 | shm_to_zero(); 175 | break; 176 | case MUSIC_PAUSE: 177 | player->pause(); 178 | shm_to_zero(); 179 | break; 180 | case CLOSE_APP: 181 | this->close(); 182 | shm_to_zero(); 183 | break; 184 | default: 185 | break; 186 | } 187 | } 188 | 189 | void MainWidget::init_shm() 190 | { 191 | shmMenuToMusic.setKey(QStringLiteral("shm_for_music")); // 音乐播放器用共享内存键值 192 | shmMusicToMenu.setKey(QStringLiteral("shm_for_menu")); // 菜单用共享内存键值 193 | switchAppTimer = new QTimer(this); 194 | detachTimer = new QTimer(this); 195 | // 定时监测共享内存并读数据 196 | readTimer = new QTimer(this); 197 | connect(readTimer, &QTimer::timeout, [&]() 198 | { 199 | qDebug() << "等待"; 200 | if(shmMenuToMusic.attach()) 201 | { 202 | int* sharedData = static_cast(shmMenuToMusic.data()); 203 | int currentNum = *sharedData; 204 | qDebug() << "MusicPlayer:" << currentNum; 205 | exec_by_num(currentNum); 206 | shmMenuToMusic.detach(); 207 | } 208 | }); 209 | readTimer->start(1000); 210 | } 211 | 212 | void MainWidget::update_btn_play() 213 | { 214 | if(player->state() == QMediaPlayer::PlayingState) 215 | ui->btnPlay->setStyleSheet(PlayingStyle()); 216 | else ui->btnPlay->setStyleSheet(PauseStyle()); 217 | } 218 | 219 | void MainWidget::set_position(int pos) 220 | { 221 | if (qAbs(player->position() - pos) > 99) 222 | player->setPosition(pos); 223 | } 224 | 225 | void MainWidget::update_duration(qint64 pos) 226 | { 227 | ui->sliderProgress->setValue(static_cast(pos)); 228 | ui->durationLabel->setText(my_format_time(pos) + "/" + my_format_time(player->duration())); 229 | if(playlist->currentIndex()>=0)ui->lyricWidget->show(pos); 230 | } 231 | 232 | void MainWidget::update_position(qint64 duration) 233 | { 234 | ui->sliderProgress->setRange(0, static_cast(duration)); 235 | ui->sliderProgress->setEnabled(static_cast(duration) > 0); 236 | if(!(static_cast(duration) > 0)) 237 | { 238 | //无音乐播放时,界面元素 239 | QImage image(":/Assets/Icon/non-music.png"); 240 | ui->coverPic->setPixmap(QPixmap::fromImage(image)); 241 | ui->musicTitleLabel->setText(u8"暂无歌曲"); 242 | ui->musicAlbumLabel->setText(u8""); 243 | ui->musicAuthorLabel->setText(u8""); 244 | ui->lyricWidget->clear(); 245 | } 246 | ui->sliderProgress->setPageStep(static_cast(duration) / 10); 247 | } 248 | 249 | void MainWidget::update_info(bool available) 250 | { 251 | if (available) 252 | { 253 | // 返回可用MP3元数据列表(调试时可以查看) 254 | // QStringList listInfo_debug = player->availableMetaData(); 255 | // qDebug() << listInfo_debug; 256 | 257 | QString info=""; 258 | QString author = player->metaData(QStringLiteral("Author")).toStringList().join(","); 259 | info.append(author); 260 | QString title = player->metaData(QStringLiteral("Title")).toString(); 261 | QString albumTitle = player->metaData(QStringLiteral("AlbumTitle")).toString(); 262 | info.append(" - "+title); 263 | info.append(" ["+my_format_time(player->duration())+"]"); 264 | 265 | //封面图片(应获取"ThumbnailImage" From: https://www.zhihu.com/question/36859497) 266 | QImage picImage = player->metaData(QStringLiteral("ThumbnailImage")).value(); 267 | if(picImage.isNull()) picImage = QImage(":/Assets/Icon/non-music.png"); 268 | ui->coverPic->setPixmap(QPixmap::fromImage(picImage)); 269 | ui->coverPic->setScaledContents(true); 270 | //改变正在播放歌曲的图标 271 | for(int i = 0; i < playlist->mediaCount(); i++) 272 | { 273 | QListWidgetItem *p = ui->musicListWidget->item(i); 274 | p->setIcon(ui->musicListWidget->get_icon()); 275 | } 276 | int index = playlist->currentIndex(); 277 | QListWidgetItem *p = ui->musicListWidget->item(index); 278 | p->setIcon(QIcon(":/Assets/Icon/music-playing.png")); 279 | 280 | // 歌词界面显示的信息 281 | ui->musicTitleLabel->setText(title); 282 | ui->musicAlbumLabel->setText(u8"专辑:"+albumTitle); 283 | ui->musicAuthorLabel->setText(u8"歌手:"+author); 284 | 285 | // 解析歌词 286 | ui->lyricWidget->process(ui->musicListWidget->musicList.musicArr[index].get_lyric()); 287 | } 288 | } 289 | 290 | void MainWidget::scan_music() 291 | { 292 | openDir = QCoreApplication::applicationDirPath() + "/Music"; 293 | QDir dirList(openDir); 294 | qDebug() << openDir; 295 | nameFile << "*.mp3" << "*.flac"; // 选择要过滤的文件格式 296 | nameFile = dirList.entryList(nameFile, QDir::Files | QDir::Readable, QDir::Name); 297 | qDebug() << "nameFile =" << nameFile << "nameFileSize =" << nameFile.size(); 298 | } 299 | 300 | void MainWidget::add_music_to_list(const QStringList& fileNames) 301 | { 302 | QListWidgetItem* item = NULL; 303 | for(int i = 0; i < fileNames.size(); i++) 304 | { 305 | ui->musicListWidget->addItem(fileNames[i]); 306 | item = ui->musicListWidget->item(i); 307 | item->setForeground(Qt::black); 308 | item->setSizeHint(QSize((float)600/1024*this->geometry().width(), (float)50/600*this->geometry().height())); 309 | item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); 310 | item->setIcon(QIcon(":/Assets/Icon/music.png")); 311 | } 312 | } 313 | 314 | void MainWidget::zoom(QPushButton* btn) 315 | { 316 | QPropertyAnimation* animation = new QPropertyAnimation(btn,"geometry"); 317 | //设置动画时间间隔 318 | animation->setDuration(400); 319 | //设置起始位置 320 | animation->setStartValue(QRect(btn->x(), btn->y()+4, btn->width(), btn->height())); 321 | //设置结束位置 322 | animation->setEndValue(QRect(btn->x(), btn->y(), btn->width(), btn->height())); 323 | //设置弹跳曲线 324 | animation->setEasingCurve(QEasingCurve::OutBounce); 325 | //执行动画 326 | animation->start(); 327 | } 328 | 329 | void MainWidget::on_btnVol_clicked() 330 | { 331 | zoom(ui->btnVol); 332 | if(ui->sliderVol->isVisible()) ui->sliderVol->setVisible(false); 333 | else ui->sliderVol->setVisible(true); 334 | } 335 | 336 | void MainWidget::on_btnQuit_clicked() 337 | { 338 | zoom(ui->btnQuit); 339 | 340 | // 向菜单的共享内存写命令 341 | if(shmMusicToMenu.isAttached()) shmMusicToMenu.detach(); 342 | if(!shmMusicToMenu.create(sizeof(int), QSharedMemory::ReadWrite)) 343 | { 344 | qDebug() << "Failed to create shared memory!"; 345 | return; 346 | } 347 | // 开始写 348 | shmMusicToMenu.lock(); 349 | int* sharedData = static_cast(shmMusicToMenu.data()); 350 | *sharedData = SWITCH_VISIBILITY; 351 | shmMusicToMenu.unlock(); 352 | 353 | // 延时detach内存 354 | detachTimer->start(1000); 355 | connect(detachTimer, &QTimer::timeout, [&]() 356 | { 357 | detachTimer->stop(); 358 | shmMusicToMenu.detach(); 359 | }); 360 | 361 | // 延时隐藏进程 362 | this->hide(); 363 | //close(); 364 | } 365 | 366 | void MainWidget::on_btnList_clicked() 367 | { 368 | zoom(ui->btnList); 369 | if(ui->stackedWidget->currentIndex() != 1) ui->stackedWidget->setCurrentIndex(1); 370 | else ui->stackedWidget->setCurrentIndex(0); 371 | } 372 | 373 | void MainWidget::on_btnPlay_clicked() 374 | { 375 | zoom(ui->btnPlay); 376 | if(player->state() == QMediaPlayer::PlayingState) 377 | { 378 | player->pause(); 379 | } 380 | else if(player->state() == QMediaPlayer::PausedState){ 381 | 382 | player->play(); 383 | } 384 | // 歌单里有歌但是是停止状态时 385 | else if((!playlist->isEmpty()) && (player->state() == QMediaPlayer::StoppedState)) 386 | { 387 | playlist->setCurrentIndex(0); 388 | player->play(); 389 | } 390 | else if(playlist->isEmpty()) return; 391 | } 392 | 393 | void MainWidget::on_btnPre_clicked() 394 | { 395 | zoom(ui->btnPre); 396 | playlist->previous(); 397 | if(!playlist->isEmpty()) player->play(); 398 | } 399 | 400 | void MainWidget::on_btnNext_clicked() 401 | { 402 | zoom(ui->btnNext); 403 | playlist->next(); 404 | if(!playlist->isEmpty()) player->play(); 405 | } 406 | 407 | void MainWidget::init_playlist() 408 | { 409 | playlist->clear(); // 先清空 410 | for(int i = 0; i < nameFile.size(); i++) 411 | { 412 | playlist->addMedia(QUrl(openDir + "/" + nameFile[i])); 413 | } 414 | } 415 | 416 | void MainWidget::on_sliderVol_valueChanged(int value) 417 | { 418 | player->setVolume(value); 419 | if(value <= 0) ui->btnVol->setStyleSheet(MuteStyle()); 420 | else ui->btnVol->setStyleSheet(VolStyle()); 421 | } 422 | 423 | void MainWidget::on_btnPlayMode_clicked() 424 | { 425 | zoom(ui->btnPlayMode); 426 | 427 | if(playlist->playbackMode() == QMediaPlaylist::Loop) 428 | { 429 | ui->btnPlayMode->setStyleSheet(RandomStyle()); 430 | playlist->setPlaybackMode(QMediaPlaylist::Random); 431 | } 432 | else if(playlist->playbackMode() == QMediaPlaylist::Random) 433 | { 434 | ui->btnPlayMode->setStyleSheet(Loop41Style()); 435 | playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); 436 | } 437 | else if(playlist->playbackMode() == QMediaPlaylist::CurrentItemInLoop) 438 | { 439 | ui->btnPlayMode->setStyleSheet(LoopStyle()); 440 | playlist->setPlaybackMode(QMediaPlaylist::Loop); 441 | } 442 | } 443 | 444 | void MainWidget::on_btnAdd_clicked() 445 | { 446 | zoom(ui->btnAdd); 447 | QFileDialog fileDialog(this); 448 | fileDialog.setAcceptMode(QFileDialog::AcceptOpen); // 设置fileDialog用来打开文件 449 | fileDialog.setFileMode(QFileDialog::ExistingFiles); 450 | fileDialog.setWindowTitle(u8"添加本地音乐(注:自动过滤,按下\"Ctrl+A\"全选添加即可;不支持添加文件夹)"); 451 | QStringList list;list<<"application/octet-stream"; // 字节流 452 | fileDialog.setMimeTypeFilters(list); 453 | qDebug() << openDir; 454 | fileDialog.setDirectory(openDir); // 设置默认指定到文件夹 455 | if (fileDialog.exec() == QDialog::Accepted) 456 | { 457 | QList urls=fileDialog.selectedUrls(); 458 | ui->musicListWidget->musicList.add_music(urls); 459 | ui->musicListWidget->refresh(); 460 | ui->stackedWidget->setCurrentIndex(1); // 切换到歌单列表 461 | } 462 | } 463 | 464 | void MainWidget::on_btnNeaten_clicked() 465 | { 466 | zoom(ui->btnNeaten); 467 | ui->musicListWidget->musicList.neaten(); 468 | ui->musicListWidget->refresh(); 469 | } 470 | 471 | void MainWidget::on_musicListWidget_clicked(const QModelIndex &index) 472 | { 473 | playlist->clear(); 474 | ui->musicListWidget->musicList.add_to_playlist(playlist); 475 | int i = index.row(); 476 | playlist->setCurrentIndex(i); 477 | player->play(); 478 | } 479 | 480 | void MainWidget::on_btnSortSinger_clicked() 481 | { 482 | zoom(ui->btnSortSinger); 483 | ui->musicListWidget->musicList.sort_by(AUTHOR); 484 | ui->musicListWidget->refresh(); 485 | } 486 | 487 | void MainWidget::on_btnSortTitle_clicked() 488 | { 489 | zoom(ui->btnSortTitle); 490 | ui->musicListWidget->musicList.sort_by(TITLE); 491 | ui->musicListWidget->refresh(); 492 | } 493 | 494 | void MainWidget::on_btnSortDuration_clicked() 495 | { 496 | zoom(ui->btnSortDuration); 497 | ui->musicListWidget->musicList.sort_by(DURATION); 498 | ui->musicListWidget->refresh(); 499 | } 500 | 501 | void MainWidget::on_btnSweep_clicked() 502 | { 503 | ui->musicListWidget->musicList.clear(); 504 | ui->musicListWidget->refresh(); 505 | } 506 | 507 | // 测试程序1:打开音乐url看看能否成功打开、有没有可用的元数据 508 | // 学到了QUrl绝对路径和相对路径该怎么表示 509 | //QUrl baseUrl("file:///D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/"); // "file:///"不可少 510 | //QUrl relativeUrl("林俊杰-进阶.mp3"); 511 | //Music msc(baseUrl.resolved(relativeUrl)); // resolved:拼接绝对变量和相对变量 512 | //Music msc(QUrl::fromLocalFile("D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/林俊杰-进阶.mp3")); // 用这样表示绝对路径也可以 513 | 514 | // 测试程序2:选择指定文件夹并筛选出指定音频文件 515 | // QCoreApplication::applicationDirPath() 为获取应用程序运行路径 516 | // QFileDialog fileDialog; 517 | // QString openDir = QCoreApplication::applicationDirPath() + "/Music"; 518 | // QDir dirList(openDir); 519 | // QStringList nameFile; 520 | // nameFile << "*.mp3" << "*.flac"; // 选择要过滤的文件格式 521 | // nameFile = dirList.entryList(nameFile, QDir::Files | QDir::Readable, QDir::Name); 522 | // qDebug() << "nameFiles == " << nameFile; 523 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MainWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "MyMusic.h" 14 | #include "MusicList.h" 15 | 16 | // 共享内存传输的一些命令 17 | #define SWITCH_VISIBILITY 1 // 改变窗体可见性 18 | #define VOL_DOWN 2 // 音量减小 19 | #define VOL_UP 3 // 音量增大 20 | #define MUSIC_PLAY 4 // 播放音乐 21 | #define MUSIC_PAUSE 5 // 暂停音乐 22 | #define CLOSE_APP 6 // 关闭APP 23 | 24 | QT_BEGIN_NAMESPACE 25 | namespace Ui { class MainWidget; } 26 | QT_END_NAMESPACE 27 | 28 | class MainWidget : public QWidget 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | MainWidget(QWidget *parent = nullptr); 34 | ~MainWidget(); 35 | // 初始化 36 | void init_ui(); // 初始化UI和按钮图标 37 | 38 | void init_play(); // 初始化播放器 39 | void init_sqlite(); // 初始化数据库sqlite 40 | void init_musiclist_widget(); // 初始化MusicListWidget的格式 41 | 42 | void init_shm(); 43 | void exec_by_num(int num); // 根据共享内存传来的命令,执行相应的任务 44 | void shm_to_zero(); // 给共享内存置0 45 | 46 | void update_btn_play(); // 切换播放按钮的QSS 47 | void set_position(int pos); 48 | void update_position(qint64 duration); 49 | void update_duration(qint64 pos); 50 | void update_info(bool available); 51 | 52 | // 自动扫描歌曲并添加到列表所用到的3种方法,暂时不用 53 | void init_playlist(); 54 | void scan_music(); 55 | void add_music_to_list(const QStringList& fileNames); 56 | 57 | void zoom(QPushButton* btn); // 按钮动画 58 | 59 | private slots: 60 | void on_btnVol_clicked(); 61 | 62 | void on_btnQuit_clicked(); 63 | 64 | void on_btnList_clicked(); 65 | 66 | void on_btnPlay_clicked(); 67 | 68 | void on_btnPre_clicked(); 69 | 70 | void on_btnNext_clicked(); 71 | 72 | void on_sliderVol_valueChanged(int value); 73 | 74 | void on_btnPlayMode_clicked(); 75 | 76 | void on_btnAdd_clicked(); 77 | 78 | void on_btnNeaten_clicked(); 79 | 80 | void on_musicListWidget_clicked(const QModelIndex &index); 81 | 82 | void on_btnSortSinger_clicked(); 83 | 84 | void on_btnSortTitle_clicked(); 85 | 86 | void on_btnSortDuration_clicked(); 87 | 88 | void on_btnSweep_clicked(); 89 | 90 | private: 91 | Ui::MainWidget *ui; 92 | 93 | QMediaPlayer* player; 94 | QMediaPlaylist* playlist; 95 | QTimer* switchAppTimer, * readTimer, * detachTimer; 96 | QSharedMemory shmMenuToMusic, shmMusicToMenu; 97 | }; 98 | #endif 99 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MainWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 600 11 | 12 | 13 | 14 | MainWidget 15 | 16 | 17 | #MainWidget{ 18 | border-image:url(:/Assets/Image/Background1.jpg) 19 | } 20 | 21 | 22 | 23 | 24 | 0 25 | 520 26 | 1024 27 | 80 28 | 29 | 30 | 31 | #frame 32 | { 33 | background:rgb(248,248,255); 34 | border-top-style:solid; 35 | border-top-width:1px; 36 | border-top-color:rgba(220, 220, 220, 1); 37 | } 38 | 39 | 40 | QFrame::StyledPanel 41 | 42 | 43 | QFrame::Raised 44 | 45 | 46 | 47 | 48 | 20 49 | 20 50 | 40 51 | 40 52 | 53 | 54 | 55 | #btnPre 56 | { 57 | image: url(:/Assets/Icon/pre.png); 58 | background-color:transparent; 59 | border:transparent; 60 | } 61 | 62 | 63 | 64 | 65 | 66 | 67 | 40 68 | 40 69 | 70 | 71 | 72 | false 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | 160 85 | 20 86 | 40 87 | 40 88 | 89 | 90 | 91 | #btnNext 92 | { 93 | image: url(:/Assets/Icon/next.png); 94 | background-color:transparent; 95 | border:transparent; 96 | } 97 | 98 | 99 | 100 | 101 | 102 | 103 | 40 104 | 40 105 | 106 | 107 | 108 | false 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | 118 | 119 | 120 | 85 121 | 15 122 | 50 123 | 50 124 | 125 | 126 | 127 | #btnPlay 128 | { 129 | image:url(:/Assets/Icon/pause.png); 130 | background-repeat:no-repeat; 131 | background-color:transparent; 132 | position:center center; 133 | border:transparent; 134 | } 135 | 136 | 137 | 138 | 139 | 140 | 141 | 40 142 | 40 143 | 144 | 145 | 146 | false 147 | 148 | 149 | true 150 | 151 | 152 | false 153 | 154 | 155 | 156 | 157 | 158 | 814 159 | 20 160 | 40 161 | 40 162 | 163 | 164 | 165 | #btnPlayMode 166 | { 167 | image:url(:/Assets/Icon/loop.png); 168 | background-repeat:no-repeat; 169 | background-color:transparent; 170 | background-position:center center; 171 | border:transparent; 172 | } 173 | 174 | 175 | 176 | 177 | 178 | 179 | 40 180 | 40 181 | 182 | 183 | 184 | false 185 | 186 | 187 | true 188 | 189 | 190 | false 191 | 192 | 193 | 194 | 195 | 196 | 235 197 | 20 198 | 400 199 | 40 200 | 201 | 202 | 203 | #sliderProgress 204 | { 205 | background:transparent; 206 | border:15px solid rgb(248,248,255); 207 | } 208 | QSlider::add-page:Horizontal 209 | { 210 | background-color: rgb(205,201,201); 211 | 212 | } 213 | QSlider::sub-page:Horizontal 214 | { 215 | background-color: rgb(255, 20, 147); 216 | } 217 | QSlider::groove:Horizontal 218 | { 219 | background:transparent; 220 | height:6px; 221 | } 222 | QSlider::handle:Horizontal 223 | { 224 | height: 18px; 225 | width: 18px; 226 | margin: -6px; 227 | border-image:url(:/Assets/Icon/sliderHandle.png); 228 | } 229 | 230 | 231 | 100 232 | 233 | 234 | 0 235 | 236 | 237 | Qt::Horizontal 238 | 239 | 240 | 241 | 242 | 243 | 963 244 | 19 245 | 42 246 | 42 247 | 248 | 249 | 250 | #btnList 251 | { 252 | image: url(:/Assets/Icon/list.png); 253 | background-color:transparent; 254 | border:transparent; 255 | } 256 | 257 | 258 | 259 | 260 | 261 | 262 | 40 263 | 40 264 | 265 | 266 | 267 | false 268 | 269 | 270 | true 271 | 272 | 273 | false 274 | 275 | 276 | 277 | 278 | 279 | 893 280 | 24 281 | 32 282 | 32 283 | 284 | 285 | 286 | #btnVol 287 | { 288 | image: url(:/Assets/Icon/vol.png); 289 | background-color:transparent; 290 | border:transparent; 291 | } 292 | 293 | 294 | 295 | 296 | 297 | 298 | 40 299 | 40 300 | 301 | 302 | 303 | false 304 | 305 | 306 | true 307 | 308 | 309 | false 310 | 311 | 312 | 313 | 314 | 315 | 640 316 | 25 317 | 140 318 | 30 319 | 320 | 321 | 322 | 323 | Dubai 324 | 12 325 | 326 | 327 | 328 | Qt::LeftToRight 329 | 330 | 331 | 00:00/00:00 332 | 333 | 334 | Qt::AlignCenter 335 | 336 | 337 | 338 | 339 | 340 | 341 | 0 342 | 0 343 | 1024 344 | 520 345 | 346 | 347 | 348 | 1 349 | 350 | 351 | 352 | 353 | 354 | 80 355 | 80 356 | 240 357 | 240 358 | 359 | 360 | 361 | #coverPic 362 | { 363 | image:url(":/Assets/Icon/non-music.png"); 364 | background-color:transparent; 365 | } 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 20 375 | 340 376 | 360 377 | 40 378 | 379 | 380 | 381 | 382 | 14 383 | 75 384 | true 385 | 386 | 387 | 388 | color:rgb(248, 248, 255); 389 | 390 | 391 | 392 | 393 | 394 | Qt::AlignCenter 395 | 396 | 397 | 398 | 399 | 400 | 20 401 | 395 402 | 360 403 | 30 404 | 405 | 406 | 407 | 408 | 10 409 | 75 410 | true 411 | 412 | 413 | 414 | color:rgb(248, 248, 255); 415 | 416 | 417 | 418 | 419 | 420 | Qt::AlignCenter 421 | 422 | 423 | 424 | 425 | 426 | 20 427 | 435 428 | 360 429 | 30 430 | 431 | 432 | 433 | 434 | 10 435 | 75 436 | true 437 | 438 | 439 | 440 | color:rgb(248, 248, 255); 441 | 442 | 443 | 444 | 445 | 446 | Qt::AlignCenter 447 | 448 | 449 | 450 | 451 | 452 | 400 453 | 80 454 | 600 455 | 385 456 | 457 | 458 | 459 | 460 | 461 | 462 | #pageMusicList 463 | { 464 | background-color: rgb(248, 248, 255); 465 | } 466 | 467 | 468 | 469 | 470 | 0 471 | 55 472 | 1024 473 | 465 474 | 475 | 476 | 477 | #musicListWidget 478 | { 479 | background-color:rgba(220, 220, 220, 0.2); 480 | outline:none; 481 | border-image:transparent; 482 | } 483 | QListWidget::item:selected:active 484 | { 485 | background:rgb(255, 20, 147); 486 | } 487 | 488 | 489 | 490 | 491 | 492 | 170 493 | 9 494 | 36 495 | 36 496 | 497 | 498 | 499 | #btnSortSinger 500 | { 501 | image: url(:/Assets/Icon/singer.png); 502 | background-color:transparent; 503 | border:transparent; 504 | } 505 | 506 | 507 | 508 | 509 | 510 | 511 | 40 512 | 40 513 | 514 | 515 | 516 | false 517 | 518 | 519 | true 520 | 521 | 522 | false 523 | 524 | 525 | 526 | 527 | 528 | 231 529 | 7 530 | 40 531 | 40 532 | 533 | 534 | 535 | #btnSortTitle 536 | { 537 | image: url(:/Assets/Icon/title.png); 538 | background-color:transparent; 539 | border:transparent; 540 | } 541 | 542 | 543 | 544 | 545 | 546 | 547 | 40 548 | 40 549 | 550 | 551 | 552 | false 553 | 554 | 555 | true 556 | 557 | 558 | false 559 | 560 | 561 | 562 | 563 | 564 | 296 565 | 11 566 | 32 567 | 32 568 | 569 | 570 | 571 | #btnSortDuration 572 | { 573 | image: url(:/Assets/Icon/duration.png); 574 | background-color:transparent; 575 | border:transparent; 576 | } 577 | 578 | 579 | 580 | 581 | 582 | 583 | 40 584 | 40 585 | 586 | 587 | 588 | false 589 | 590 | 591 | true 592 | 593 | 594 | false 595 | 596 | 597 | 598 | 599 | 600 | 20 601 | 7 602 | 40 603 | 40 604 | 605 | 606 | 607 | #btnAdd 608 | { 609 | image: url(:/Assets/Icon/folder.png); 610 | background-color:transparent; 611 | border:transparent; 612 | } 613 | 614 | 615 | 616 | 617 | 618 | 619 | 40 620 | 40 621 | 622 | 623 | 624 | false 625 | 626 | 627 | true 628 | 629 | 630 | false 631 | 632 | 633 | 634 | 635 | 636 | 85 637 | 7 638 | 40 639 | 40 640 | 641 | 642 | 643 | #btnNeaten 644 | { 645 | image: url(:/Assets/Icon/neaten.png); 646 | background-color:transparent; 647 | border:transparent; 648 | } 649 | 650 | 651 | 652 | 653 | 654 | 655 | 40 656 | 40 657 | 658 | 659 | 660 | false 661 | 662 | 663 | true 664 | 665 | 666 | false 667 | 668 | 669 | 670 | 671 | 672 | 358 673 | 7 674 | 40 675 | 40 676 | 677 | 678 | 679 | #btnSweep 680 | { 681 | image: url(:/Assets/Icon/sweep.png); 682 | background-color:transparent; 683 | border:transparent; 684 | } 685 | 686 | 687 | 688 | 689 | 690 | 691 | 40 692 | 40 693 | 694 | 695 | 696 | false 697 | 698 | 699 | true 700 | 701 | 702 | false 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 884 711 | 320 712 | 50 713 | 200 714 | 715 | 716 | 717 | #sliderVol 718 | { 719 | background-color:rgb(248,248,255); 720 | min-width:20px; 721 | max-width:20px; 722 | border:15px solid rgb(248,248,255); 723 | } 724 | QSlider::add-page:vertical 725 | { 726 | background-color: rgb(255, 20, 147); 727 | } 728 | QSlider::sub-page:vertical 729 | { 730 | background-color: rgb(205,201,201); 731 | } 732 | QSlider::groove:vertical 733 | { 734 | background:transparent; 735 | width:8px; 736 | } 737 | QSlider::handle:vertical 738 | { 739 | height: 18px; 740 | width: 18px; 741 | margin: -5px; 742 | border-image:url(:/Assets/Icon/sliderHandle.png); 743 | } 744 | 745 | 746 | 100 747 | 748 | 749 | 50 750 | 751 | 752 | Qt::Vertical 753 | 754 | 755 | 756 | 757 | 758 | 984 759 | 15 760 | 25 761 | 25 762 | 763 | 764 | 765 | Qt::DefaultContextMenu 766 | 767 | 768 | #btnQuit 769 | { 770 | image: url(:/Assets/Icon/quit.png); 771 | background-color:transparent; 772 | border:transparent; 773 | } 774 | 775 | 776 | 777 | 778 | 779 | 780 | 20 781 | 20 782 | 783 | 784 | 785 | false 786 | 787 | 788 | false 789 | 790 | 791 | false 792 | 793 | 794 | false 795 | 796 | 797 | frame 798 | stackedWidget 799 | btnQuit 800 | sliderVol 801 | 802 | 803 | 804 | MusicListWidget 805 | QListWidget 806 |
musiclistwidget.h
807 |
808 | 809 | LyricWidget 810 | QWidget 811 |
lyricwidget.h
812 | 1 813 |
814 |
815 | 816 | 817 |
818 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MusicList.cpp: -------------------------------------------------------------------------------- 1 | #include "MusicList.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "MusicListWidget.h" 7 | #include 8 | #include 9 | #include 10 | 11 | MusicList::MusicList(const QList &urls) 12 | { 13 | add_music(urls); 14 | } 15 | 16 | void MusicList::add_music(const QList &urls) 17 | { 18 | //实测这里耗时较长,所以添加一个进度显示对话框 19 | QProgressDialog proDialog(u8"添加进度",u8"取消",0,urls.size()); 20 | proDialog.setMinimumSize(350,150); 21 | proDialog.setWindowModality(Qt::WindowModal); 22 | proDialog.setWindowTitle("添加中...请稍后"); 23 | proDialog.show(); 24 | int x=0; 25 | foreach (QUrl i, urls) { 26 | x++; 27 | proDialog.setValue(x); 28 | //过滤Url的类型 29 | QMimeDatabase db; 30 | QMimeType mime = db.mimeTypeForFile(i.toLocalFile()); 31 | if(mime.name()!="audio/mpeg" && mime.name()!="audio/flac") 32 | { 33 | continue; 34 | } 35 | //剩下的符合类型 36 | musicArr.push_back(MyMusic(i)); 37 | musicArr[musicArr.size() - 1].insert_sql(); // 放入数据库 38 | if(proDialog.wasCanceled()) break; 39 | } 40 | } 41 | 42 | void MusicList::add_music(const MyMusic &iMusic) 43 | { 44 | musicArr.push_back(iMusic); 45 | musicArr[musicArr.size() - 1].insert_sql(); // 放入数据库 46 | } 47 | 48 | MyMusic MusicList::get_music(int pos) 49 | { 50 | return musicArr[pos]; 51 | } 52 | 53 | void MusicList::add_to_playlist(QMediaPlaylist* playlist) 54 | { 55 | for(auto i=musicArr.begin();i!=musicArr.end();i++) 56 | { 57 | playlist->addMedia(i->get_url()); 58 | } 59 | } 60 | 61 | void MusicList::add_to_listwidget(MusicListWidget* listWidget) 62 | { 63 | foreach(const MyMusic &i, musicArr) 64 | { 65 | QListWidgetItem *item = new QListWidgetItem; 66 | item->setIcon(listWidget->get_icon()); 67 | item->setText(i.get_brief_info()); 68 | item->setForeground(Qt::black); 69 | item->setSizeHint(QSize(1000, 70)); 70 | item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); 71 | listWidget->addItem(item); 72 | } 73 | } 74 | 75 | void MusicList::sort_by(COMPARE key) 76 | { 77 | sort(musicArr.begin(), musicArr.end(), MusicCompare(key)); 78 | remove_sql_all(); 79 | insert_sql_all(); 80 | } 81 | 82 | void MusicList::neaten() 83 | { 84 | sort(musicArr.begin(), musicArr.end(), MusicCompare(DEFAULT)); 85 | musicArr.erase(unique(musicArr.begin(), musicArr.end(), MusicCompare(EQUALITY)), musicArr.end()); 86 | remove_sql_all(); 87 | insert_sql_all(); 88 | } 89 | 90 | void MusicList::clear() 91 | { 92 | musicArr.clear(); 93 | remove_sql_all(); 94 | } 95 | 96 | void MusicList::remove_sql_all() 97 | { 98 | QSqlQuery sqlQuery; 99 | QString deleteSql = "delete from MusicInfo"; 100 | sqlQuery.prepare(deleteSql); 101 | sqlQuery.exec(); 102 | } 103 | 104 | void MusicList::insert_sql_all() 105 | { 106 | for(auto i = musicArr.begin(); i < musicArr.end(); ++i) 107 | i->insert_sql(); 108 | } 109 | 110 | void MusicList::read_from_sql() 111 | { 112 | QSqlQuery sqlQuery; 113 | QString selectSql = "select url, author, title, duration, albumTitle, bitRate from MusicInfo"; 114 | sqlQuery.prepare(selectSql); 115 | if(sqlQuery.exec()) 116 | { 117 | while(sqlQuery.next()) 118 | { 119 | MyMusic tempMusic; 120 | tempMusic.url = QUrl(sqlQuery.value(0).toString()); 121 | tempMusic.author = sqlQuery.value(1).toString(); 122 | tempMusic.title = sqlQuery.value(2).toString(); 123 | tempMusic.duration = sqlQuery.value(3).toLongLong(); 124 | tempMusic.albumTitle = sqlQuery.value(4).toString(); 125 | tempMusic.bitRate = sqlQuery.value(5).toInt(); 126 | musicArr.push_back(tempMusic); 127 | qDebug() << tempMusic.author << "-" << tempMusic.title << "-" << tempMusic.duration; // 测试用 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MusicList.h: -------------------------------------------------------------------------------- 1 | #ifndef MUSICLIST_H 2 | #define MUSICLIST_H 3 | #include "MyMusic.h" 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class MusicListWidget; 10 | 11 | class MusicList 12 | { 13 | private: 14 | // 所存储的歌曲 15 | vector musicArr; 16 | public: 17 | MusicList() {} 18 | MusicList(const QList& urls); 19 | 20 | // 从url添加歌曲 21 | void add_music(const QList& urls); 22 | 23 | // 添加一首歌曲 24 | void add_music(const MyMusic& iMusic); 25 | 26 | //获取指定位置的歌曲 27 | MyMusic get_music(int pos); 28 | 29 | // 将本歌单加入播放列表 30 | void add_to_playlist(QMediaPlaylist* playlist); 31 | 32 | // 歌单可视化 33 | void add_to_listwidget(MusicListWidget* listWidget); 34 | 35 | // 将本列表中的歌曲排序 36 | void sort_by(COMPARE key); 37 | 38 | // 整理歌单,去掉重复的 39 | void neaten(); 40 | 41 | // 清空本歌单 42 | void clear(); 43 | 44 | // 数据库移除全部歌曲 45 | void remove_sql_all(); 46 | 47 | // 数据库加入全部歌曲 48 | void insert_sql_all(); 49 | 50 | // 从数据库中恢复歌单 51 | void read_from_sql(); 52 | 53 | friend class MainWidget; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MusicListWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MusicListWidget.h" 2 | 3 | MusicListWidget::MusicListWidget(QWidget* parent) : QListWidget(parent) 4 | { 5 | 6 | } 7 | 8 | void MusicListWidget::refresh() 9 | { 10 | clear(); 11 | musicList.add_to_listwidget(this); 12 | } 13 | 14 | void MusicListWidget::set_musiclist(const MusicList& music) 15 | { 16 | musicList = music; 17 | refresh(); 18 | } 19 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MusicListWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MUSICLISTWIDGET_H 2 | #define MUSICLISTWIDGET_H 3 | #include 4 | #include "MusicList.h" 5 | 6 | class MusicListWidget : public QListWidget 7 | { 8 | private: 9 | //当前歌曲列表(存储的是歌曲信息) 10 | MusicList musicList; 11 | //当前展示列表项使用的图标 12 | QIcon icon=QIcon(":/Assets/Icon/music.png"); 13 | public: 14 | MusicListWidget(QWidget* parent = Q_NULLPTR); 15 | 16 | //刷新显示(当musicList有所变化时,需要调用) 17 | void refresh(); 18 | //设置歌曲列表 19 | void set_musiclist(const MusicList& music); 20 | 21 | //设置/获取图标Icon 22 | void set_icon(QIcon iicon){ icon=iicon; } 23 | QIcon get_icon(){ return icon; } 24 | 25 | friend class MainWidget; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MusicPlayerWW.pro: -------------------------------------------------------------------------------- 1 | QT += core gui sql 2 | QT += multimedia 3 | 4 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 5 | 6 | CONFIG += c++11 7 | 8 | # The following define makes your compiler emit warnings if you use 9 | # any Qt feature that has been marked deprecated (the exact warnings 10 | # depend on your compiler). Please consult the documentation of the 11 | # deprecated API in order to know how to port your code away from it. 12 | DEFINES += QT_DEPRECATED_WARNINGS 13 | 14 | # You can also make your code fail to compile if it uses deprecated APIs. 15 | # In order to do so, uncomment the following line. 16 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 17 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 18 | 19 | SOURCES += \ 20 | LyricWidget.cpp \ 21 | MusicList.cpp \ 22 | MusicListWidget.cpp \ 23 | MyMusic.cpp \ 24 | main.cpp \ 25 | MainWidget.cpp 26 | 27 | HEADERS += \ 28 | LyricWidget.h \ 29 | MainWidget.h \ 30 | MusicList.h \ 31 | MusicListWidget.h \ 32 | MyMusic.h \ 33 | MyQSS.h 34 | 35 | FORMS += \ 36 | LyricWidget.ui \ 37 | MainWidget.ui 38 | 39 | # Default rules for deployment. 40 | qnx: target.path = /tmp/$${TARGET}/bin 41 | else: unix:!android: target.path = /opt/$${TARGET}/bin 42 | !isEmpty(target.path): INSTALLS += target 43 | 44 | RESOURCES += \ 45 | Resources.qrc 46 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MyMusic.cpp: -------------------------------------------------------------------------------- 1 | #include "MyMusic.h" 2 | #include 3 | #include // 提供事件循环 4 | #include 5 | #include 6 | 7 | MyMusic::MyMusic(QUrl iurl) 8 | { 9 | url = iurl; 10 | // qDebug() << url.toString(); 11 | init_from_url(); 12 | } 13 | 14 | MyMusic::MyMusic(QString musicName) 15 | { 16 | init_from_name(musicName); 17 | } 18 | 19 | QString my_format_time(qint64 msTime) 20 | { 21 | qint64 seconds = msTime / 1000; // 毫秒变为秒 22 | const qint64 minutes = seconds / 60; 23 | seconds -= minutes * 60; 24 | return QStringLiteral("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')); 25 | } 26 | 27 | void MyMusic::init_from_url() 28 | { 29 | QMediaPlayer tmpPlayer; 30 | tmpPlayer.setMedia(url); 31 | // 元数据的解析需要时间,所以这里需要循环等待 (但同时需要保持Qt事件处理机制在运行) 32 | while(!tmpPlayer.isMetaDataAvailable()) QCoreApplication::processEvents(); 33 | QString list = tmpPlayer.availableMetaData().join(", "); 34 | if(tmpPlayer.isMetaDataAvailable()) 35 | { 36 | author = tmpPlayer.metaData(QStringLiteral("Author")).toStringList().join(","); // 作者这里返回的是StringList 37 | title = tmpPlayer.metaData(QStringLiteral("Title")).toString(); 38 | albumTitle = tmpPlayer.metaData(QStringLiteral("AlbumTitle")).toString(); 39 | duration = tmpPlayer.duration(); 40 | bitRate = tmpPlayer.metaData(QStringLiteral("AudioBitRate")).toInt(); 41 | // 封面图片(应获取"ThumbnailImage" From: https://www.zhihu.com/question/36859497) 42 | picImg = tmpPlayer.metaData(QStringLiteral("ThumbnailImage")).value(); 43 | if(picImg.isNull()) picImg = QImage(":/Assets/Icon/non-music.png"); 44 | } 45 | } 46 | 47 | void MyMusic::init_from_name(QString musicName) 48 | { 49 | QString openDir = QCoreApplication::applicationDirPath() + "/Music"; 50 | url = openDir + "/" + musicName; 51 | init_from_url(); 52 | } 53 | 54 | QString MyMusic::get_brief_info() const 55 | { 56 | return author + " - " + title + " [" + my_format_time(duration) + "]"; 57 | } 58 | 59 | QString MyMusic::get_lyric() 60 | { 61 | QString filePath = url.toLocalFile(); 62 | filePath.replace(".mp3", ".lrc"); 63 | filePath.replace(".flac", ".lrc"); 64 | filePath.replace(".mpga", ".lrc"); 65 | 66 | return filePath; 67 | } 68 | 69 | void MyMusic::insert_sql() 70 | { 71 | QSqlQuery sqlQuery; 72 | QString insertSql = "insert into MusicInfo values (?, ?, ?, ?, ?, ?)"; 73 | sqlQuery.prepare(insertSql); 74 | sqlQuery.addBindValue(url.toString()); 75 | sqlQuery.addBindValue(author); 76 | sqlQuery.addBindValue(title); 77 | sqlQuery.addBindValue(duration); 78 | sqlQuery.addBindValue(albumTitle); 79 | sqlQuery.addBindValue(bitRate); 80 | if(sqlQuery.exec()) qDebug() << "插入数据库"; // 测试用 81 | } 82 | 83 | bool MusicCompare::operator()(const QString& strA, const QString& strB) 84 | { 85 | MyMusic A(strA); 86 | MyMusic B(strB); 87 | switch (key) { 88 | case TITLE: 89 | return A.title < B.title; 90 | case AUTHOR: 91 | return A.author < B.author; 92 | case DURATION: 93 | return A.duration < B.duration; 94 | case EQUALITY: // 对比两者url是否相等 95 | return A.get_url() == B.get_url(); 96 | default: 97 | return A.get_brief_info() < B.get_brief_info(); 98 | } 99 | } 100 | 101 | bool MusicCompare::operator()(const MyMusic &A, const MyMusic &B) 102 | { 103 | switch (key) { 104 | case TITLE: 105 | return A.title < B.title; 106 | case AUTHOR: 107 | return A.author < B.author; 108 | case DURATION: 109 | return A.duration < B.duration; 110 | case EQUALITY: // 对比两者url是否相等 111 | return A.get_url() == B.get_url(); 112 | default: 113 | return A.get_brief_info() < B.get_brief_info(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MyMusic.h: -------------------------------------------------------------------------------- 1 | #ifndef MYMUSIC_H 2 | #define MYMUSIC_H 3 | #include 4 | #include 5 | #include 6 | 7 | // 歌曲类;用于描述单个歌曲 8 | class MyMusic 9 | { 10 | private: 11 | QUrl url; // 歌曲地址 12 | QString author; // 作者 13 | QString title; // 歌名 14 | QString albumTitle; // 唱片集 15 | qint64 duration; // 时长 16 | int bitRate; // 比特率 17 | QImage picImg; // 专辑封面 18 | 19 | void init_from_url(); // 私有方法:根据Url获取歌曲各信息 20 | void init_from_name(QString musicName); // 私有方法:根据歌曲名获取歌曲各信息 21 | 22 | public: 23 | MyMusic() {} 24 | MyMusic(QUrl iurl); 25 | MyMusic(QString musicName); 26 | 27 | QUrl get_url() const {return url;} // 返回歌曲地址 28 | QString get_brief_info() const; // 以一行返回歌曲的信息 29 | QString get_lyric(); // 根据文件名来获取歌词路径 30 | void insert_sql(); // 添加至数据库 31 | 32 | friend class MusicCompare; 33 | friend class MusicList; 34 | friend class MainWidget; 35 | }; 36 | 37 | // 排序的可选属性 38 | enum COMPARE{DEFAULT, TITLE, AUTHOR, DURATION, EQUALITY}; 39 | 40 | // 排序对比类:用于 41 | class MusicCompare 42 | { 43 | COMPARE key; 44 | public: 45 | MusicCompare(){key=DEFAULT;} 46 | MusicCompare(COMPARE ikey){key=ikey;} 47 | bool operator()(const MyMusic& A, const MyMusic& B); 48 | bool operator()(const QString& strA, const QString& strB); 49 | }; 50 | 51 | #endif // MYMUSIC_H 52 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/MyQSS.h: -------------------------------------------------------------------------------- 1 | #ifndef MYQSS_H 2 | #define MYQSS_H 3 | #include 4 | 5 | // 此文件统一存放一些QSS样式代码 6 | 7 | // 播放中的按钮样式 8 | inline QString PlayingStyle() 9 | { 10 | return 11 | "#btnPlay" 12 | "{" 13 | "image:url(:/Assets/Icon/playing.png);" 14 | "background-repeat:no-repeat;" 15 | "background-color:transparent;" 16 | "background-position:center center;" 17 | "border:transparent;" 18 | "}"; 19 | } 20 | // 未在播放的按钮状态 21 | inline QString PauseStyle() 22 | { 23 | return 24 | "#btnPlay" 25 | "{" 26 | "image:url(:/Assets/Icon/pause.png);" 27 | "background-repeat:no-repeat;" 28 | "background-color:transparent;" 29 | "background-position:center center;" 30 | "border:transparent;" 31 | "}"; 32 | } 33 | 34 | // 音量按钮 35 | inline QString VolStyle() 36 | { 37 | return 38 | "#btnVol" 39 | "{" 40 | "image:url(:/Assets/Icon/vol.png);" 41 | "background-repeat:no-repeat;" 42 | "background-color:transparent;" 43 | "background-position:center center;" 44 | "border:transparent;" 45 | "}"; 46 | } 47 | // 音量按钮静音状态 48 | inline QString MuteStyle() 49 | { 50 | return 51 | "#btnVol" 52 | "{" 53 | "image:url(:/Assets/Icon/mute.png);" 54 | "background-repeat:no-repeat;" 55 | "background-color:transparent;" 56 | "background-position:center center;" 57 | "border:transparent;" 58 | "}"; 59 | } 60 | 61 | // 播放模式的切换 62 | // 随机 63 | inline QString RandomStyle() 64 | { 65 | return 66 | "#btnPlayMode" 67 | "{" 68 | "image:url(:/Assets/Icon/random.png);" 69 | "background-repeat:no-repeat;" 70 | "background-color:transparent;" 71 | "background-position:center center;" 72 | "border:transparent;" 73 | "}"; 74 | } 75 | // 列表循环 76 | inline QString LoopStyle() 77 | { 78 | return 79 | "#btnPlayMode" 80 | "{" 81 | "image:url(:/Assets/Icon/loop.png);" 82 | "background-repeat:no-repeat;" 83 | "background-color:transparent;" 84 | "background-position:center center;" 85 | "border:transparent;" 86 | "}"; 87 | } 88 | // 单曲循环 89 | inline QString Loop41Style() 90 | { 91 | return 92 | "#btnPlayMode" 93 | "{" 94 | "image:url(:/Assets/Icon/loop41.png);" 95 | "background-repeat:no-repeat;" 96 | "background-color:transparent;" 97 | "background-position:center center;" 98 | "border:transparent;" 99 | "}"; 100 | } 101 | 102 | inline QString ScrollBarStyle() 103 | { 104 | return 105 | "QScrollBar:vertical" 106 | "{" 107 | "width:80px;" 108 | "background:rgba(248, 248, 255, 0);" 109 | "margin:0px,0px,0px,0px;" 110 | "padding-top:80px;" 111 | "padding-bottom:80px;" 112 | "}" 113 | // 滑块区域 114 | "QScrollBar::handle:vertical" 115 | "{" 116 | "background:rgba(255, 20, 147, 1);" 117 | "min-height:150px;" 118 | // "border-radius: 40px;" 119 | "}" 120 | // 上箭头 121 | "QScrollBar::sub-line:vertical" 122 | "{" 123 | "width:80px;" 124 | "height:80px;" 125 | "border-image:url(:/Assets/Icon/up-arrow.png);" 126 | "subcontrol-position:top;" 127 | "}" 128 | // 下箭头 129 | "QScrollBar::add-line:vertical" 130 | "{" 131 | "width:80px;" 132 | "height:80px;" 133 | "border-image:url(:/Assets/Icon/down-arrow.png);" 134 | "subcontrol-position:bottom;" 135 | "}" 136 | // 滑块以外的区域 137 | "QScrollBar::sub-page:vertical" 138 | "{" 139 | "background:rgb(220, 220, 220);" 140 | "}" 141 | "QScrollBar::add-page:vertical" 142 | "{" 143 | "background:rgb(220, 220, 220);" 144 | "}" 145 | ; 146 | } 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assets/Image/Background1.jpg 4 | Assets/Icon/next.png 5 | Assets/Icon/pause.png 6 | Assets/Icon/pre.png 7 | Assets/Icon/sliderHandle.png 8 | Assets/Icon/vol.png 9 | Assets/Icon/quit.png 10 | Assets/Icon/music.png 11 | Assets/Icon/music-playing.png 12 | Assets/Icon/playing.png 13 | Assets/Icon/non-music.png 14 | Assets/Icon/list.png 15 | Assets/Icon/title.png 16 | Assets/Icon/singer.png 17 | Assets/Icon/duration.png 18 | Assets/Icon/mute.png 19 | Assets/Icon/random.png 20 | Assets/Icon/loop.png 21 | Assets/Icon/loop41.png 22 | Assets/Icon/folder.png 23 | Assets/Icon/neaten.png 24 | Assets/Icon/up-arrow.png 25 | Assets/Icon/down-arrow.png 26 | Assets/Icon/sweep.png 27 | 28 | 29 | -------------------------------------------------------------------------------- /arm/MusicPlayerWW/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWidget w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/bluetooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/bluetooth.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/car.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/map.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/music.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/phonecall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/phonecall.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/settings.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Icon/weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Icon/weather.png -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Assets/Image/background-main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/CarPlayMainWW/Assets/Image/background-main.jpg -------------------------------------------------------------------------------- /x86/CarPlayMainWW/CarPlayMainWW.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 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 | Threads.cpp \ 20 | main.cpp \ 21 | MainWidget.cpp 22 | 23 | HEADERS += \ 24 | MainWidget.h \ 25 | MyQSS.h \ 26 | Threads.h 27 | 28 | FORMS += \ 29 | MainWidget.ui 30 | 31 | # Default rules for deployment. 32 | qnx: target.path = /tmp/$${TARGET}/bin 33 | else: unix:!android: target.path = /opt/$${TARGET}/bin 34 | !isEmpty(target.path): INSTALLS += target 35 | 36 | RESOURCES += \ 37 | Resources.qrc 38 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/MainWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | #include "ui_MainWidget.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "MyQSS.h" 9 | 10 | #ifdef Q_OS_WINDOWS 11 | QString musicAppPath = QDir::currentPath() + "/MusicPlayerWW.exe"; // 音乐播放器app的位置 12 | #endif 13 | #ifdef Q_OS_LINUX 14 | QString musicAppPath = QDir::currentPath() + "/MusicPlayerWW"; 15 | #endif 16 | 17 | int preNum = 0; 18 | 19 | MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget) 20 | { 21 | ui->setupUi(this); 22 | 23 | proMusic = new QProcess(); 24 | proMusic->start(musicAppPath, QStringList("-hide")); 25 | init_ui(); 26 | init_shm(); 27 | } 28 | 29 | MainWidget::~MainWidget() 30 | { 31 | qDebug() << "关闭"; 32 | proMusic->close(); 33 | delete ui; 34 | } 35 | 36 | void MainWidget::init_ui() 37 | { 38 | setFixedSize(1024, 600); 39 | setAttribute(Qt::WA_StyledBackground); // 告诉窗口,应该使用设置的样式表进行渲染控件背景 40 | setWindowFlags(Qt::FramelessWindowHint); // 去除标题栏 41 | ui->dateLabel->setStyleSheet(DateLabelStyle()); // 设置日期标签的QSS 42 | ui->timeLabel->setStyleSheet(TimeLabelStyle()); // 设置时间标签的QSS 43 | 44 | // 线程:获取当前日期时间并显示 45 | timeShowThread = new Thread_DateTime(ui->dateLabel, ui->timeLabel); 46 | timeShowThread->start(); 47 | connect(timeShowThread, &QThread::finished, timeShowThread, &QObject::deleteLater); 48 | } 49 | 50 | void MainWidget::shm_to_zero() 51 | { 52 | // 开始写 53 | shmMusicToMenu.lock(); 54 | int* sharedData = static_cast(shmMusicToMenu.data()); 55 | *sharedData = 0; 56 | shmMusicToMenu.unlock(); 57 | shmMusicToMenu.detach(); 58 | } 59 | 60 | void MainWidget::exec_by_num(int num) 61 | { 62 | switch(num) 63 | { 64 | case SWITCH_VISIBILITY: 65 | this->show(); 66 | shm_to_zero(); 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | void MainWidget::init_shm() 74 | { 75 | shmMenuToMusic.setKey(QStringLiteral("shm_for_music")); // 音乐播放器用共享内存键值 76 | shmMusicToMenu.setKey(QStringLiteral("shm_for_menu")); // 菜单用共享内存键值 77 | QSystemSemaphore semToMusic("sem_to_music", 0); 78 | QSystemSemaphore semToMenu("sem_to_menu", 0); 79 | detachTimer = new QTimer(this); 80 | // 定时监测共享内存并读数据 81 | readTimer = new QTimer(this); 82 | connect(readTimer, &QTimer::timeout, [&]() 83 | { 84 | // qDebug() << "等待"; 85 | if(shmMusicToMenu.attach()) 86 | { 87 | int* sharedData = static_cast(shmMusicToMenu.data()); 88 | int currentNum = *sharedData; 89 | qDebug() << "Menu:" << currentNum; 90 | exec_by_num(currentNum); 91 | shmMusicToMenu.detach(); 92 | } 93 | }); 94 | readTimer->start(1000); 95 | } 96 | 97 | void MainWidget::zoom(QPushButton* btn) 98 | { 99 | QPropertyAnimation* animation = new QPropertyAnimation(btn,"geometry"); 100 | //设置动画时间间隔 101 | animation->setDuration(400); 102 | //设置起始位置 103 | animation->setStartValue(QRect(btn->x(), btn->y()+4, btn->width(), btn->height())); 104 | //设置结束位置 105 | animation->setEndValue(QRect(btn->x(), btn->y(), btn->width(), btn->height())); 106 | //设置弹跳曲线 107 | animation->setEasingCurve(QEasingCurve::OutBounce); 108 | //执行动画 109 | animation->start(); 110 | } 111 | 112 | void MainWidget::on_btnQuit_clicked() 113 | { 114 | zoom(ui->btnQuit); 115 | // sr04_close(); 116 | timeShowThread->stop(); 117 | close(); 118 | } 119 | 120 | void MainWidget::on_btnMusic_clicked() 121 | { 122 | zoom(ui->btnMusic); 123 | // 判断音乐播放器有没有在运行 124 | if(proMusic->state() == QProcess::Running) // 如果音乐进程在 125 | { 126 | if(shmMenuToMusic.isAttached()) shmMenuToMusic.detach(); // 如果之前绑定有,解绑并创建新的 127 | if(!shmMenuToMusic.create(sizeof(int), QSharedMemory::ReadWrite)) 128 | { 129 | qDebug() << "Failed to create shared memory!"; 130 | return; 131 | } 132 | // 开始写 133 | shmMenuToMusic.lock(); 134 | int* sharedData = static_cast(shmMenuToMusic.data()); 135 | *sharedData = SWITCH_VISIBILITY; 136 | shmMenuToMusic.unlock(); 137 | 138 | // 延时detach内存 139 | detachTimer->start(1000); 140 | connect(detachTimer, &QTimer::timeout, [&]() 141 | { 142 | detachTimer->stop(); 143 | shmMenuToMusic.detach(); 144 | }); 145 | 146 | // 延时隐藏进程 147 | this->hide(); 148 | } 149 | else if(proMusic->state() == QProcess::NotRunning) // 如果没开过音乐进程 150 | { 151 | proMusic->close(); 152 | proMusic->start(musicAppPath); 153 | // 延时隐藏进程 154 | this->hide(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/MainWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Threads.h" 14 | 15 | // 共享内存传输的一些命令 16 | #define SWITCH_VISIBILITY 1 // 改变窗体可见性 17 | #define VOL_DOWN 2 // 音量减小 18 | #define VOL_UP 3 // 音量增大 19 | #define MUSIC_PLAY 4 // 播放音乐 20 | #define MUSIC_PAUSE 5 // 暂停音乐 21 | #define CLOSE_APP 6 // 关闭APP 22 | 23 | QT_BEGIN_NAMESPACE 24 | namespace Ui { class MainWidget; } 25 | QT_END_NAMESPACE 26 | 27 | class MainWidget : public QWidget 28 | { 29 | Q_OBJECT 30 | 31 | public: 32 | MainWidget(QWidget *parent = nullptr); 33 | ~MainWidget(); 34 | 35 | void init_ui(); 36 | void init_shm(); 37 | void exec_by_num(int num); 38 | void shm_to_zero(); 39 | 40 | void zoom(QPushButton* btn); // 按钮动画 41 | 42 | private slots: 43 | void on_btnQuit_clicked(); 44 | 45 | void on_btnMusic_clicked(); 46 | 47 | private: 48 | Ui::MainWidget *ui; 49 | QTimer* timerDT, * switchAppTimer, * readTimer, * detachTimer; 50 | QProcess* proMusic; 51 | Thread_DateTime* timeShowThread; 52 | QSharedMemory shmMenuToMusic, shmMusicToMenu; 53 | }; 54 | 55 | #endif // MAINWIDGET_H 56 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/MainWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 600 11 | 12 | 13 | 14 | MainWidget 15 | 16 | 17 | #MainWidget 18 | { 19 | background-image: url(:/Assets/Image/background-main.jpg); 20 | } 21 | 22 | 23 | 24 | 25 | 730 26 | 40 27 | 220 28 | 140 29 | 30 | 31 | 32 | false 33 | 34 | 35 | #dateTimeGroupBox 36 | { 37 | background:transparent; 38 | border:transparent; 39 | } 40 | 41 | 42 | 43 | 44 | 45 | Qt::AlignCenter 46 | 47 | 48 | false 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 0 57 | 0 58 | 220 59 | 60 60 | 61 | 62 | 63 | 64 | 65 | 66 | Qt::AlignCenter 67 | 68 | 69 | 70 | 71 | 72 | 0 73 | 60 74 | 220 75 | 80 76 | 77 | 78 | 79 | 80 | 81 | 82 | Qt::AlignCenter 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 520 91 | 1024 92 | 80 93 | 94 | 95 | 96 | #frame 97 | { 98 | background:rgb(248,248,255); 99 | border-top-style:solid; 100 | border-top-width:1px; 101 | border-top-color:rgba(220, 220, 220, 1); 102 | } 103 | 104 | 105 | QFrame::StyledPanel 106 | 107 | 108 | QFrame::Raised 109 | 110 | 111 | 112 | 113 | 0 114 | 0 115 | 220 116 | 80 117 | 118 | 119 | 120 | 121 | 15 122 | 75 123 | true 124 | 125 | 126 | 127 | #btnMusic 128 | { 129 | border-style:solid; 130 | border-color:rgb(0, 0, 0); 131 | border-right-width:2px; 132 | background:rgba(248, 248, 255, 0); 133 | } 134 | 135 | 136 | 本地音乐 137 | 138 | 139 | 140 | :/Assets/Icon/music.png:/Assets/Icon/music.png 141 | 142 | 143 | 144 | 30 145 | 30 146 | 147 | 148 | 149 | false 150 | 151 | 152 | false 153 | 154 | 155 | false 156 | 157 | 158 | false 159 | 160 | 161 | false 162 | 163 | 164 | 165 | 166 | 167 | 220 168 | 0 169 | 220 170 | 80 171 | 172 | 173 | 174 | 175 | 15 176 | 75 177 | true 178 | 179 | 180 | 181 | #btnIdle1 182 | { 183 | border-style:solid; 184 | border-color:rgb(0, 0, 0); 185 | border-left-width:2px; 186 | border-right-width:2px; 187 | background:rgba(248, 248, 255, 0); 188 | } 189 | 190 | 191 | 192 | 193 | 194 | 195 | 30 196 | 30 197 | 198 | 199 | 200 | false 201 | 202 | 203 | false 204 | 205 | 206 | false 207 | 208 | 209 | false 210 | 211 | 212 | false 213 | 214 | 215 | 216 | 217 | 218 | 440 219 | 0 220 | 220 221 | 80 222 | 223 | 224 | 225 | 226 | 15 227 | 75 228 | true 229 | 230 | 231 | 232 | #btnIdle2 233 | { 234 | border-style:solid; 235 | border-color:rgb(0, 0, 0); 236 | border-left-width:2px; 237 | border-right-width:2px; 238 | background:rgba(248, 248, 255, 0); 239 | } 240 | 241 | 242 | 243 | 244 | 245 | 246 | 30 247 | 30 248 | 249 | 250 | 251 | false 252 | 253 | 254 | false 255 | 256 | 257 | false 258 | 259 | 260 | false 261 | 262 | 263 | false 264 | 265 | 266 | 267 | 268 | 269 | 660 270 | 0 271 | 220 272 | 80 273 | 274 | 275 | 276 | 277 | 15 278 | 75 279 | true 280 | 281 | 282 | 283 | #btnIdle3 284 | { 285 | border-style:solid; 286 | border-color:rgb(0, 0, 0); 287 | border-left-width:2px; 288 | border-right-width:2px; 289 | background:rgba(248, 248, 255, 0); 290 | } 291 | 292 | 293 | 294 | 295 | 296 | 297 | 30 298 | 30 299 | 300 | 301 | 302 | false 303 | 304 | 305 | false 306 | 307 | 308 | false 309 | 310 | 311 | false 312 | 313 | 314 | false 315 | 316 | 317 | 318 | 319 | 320 | 880 321 | 0 322 | 144 323 | 80 324 | 325 | 326 | 327 | 328 | 15 329 | 75 330 | true 331 | 332 | 333 | 334 | #btnQuit 335 | { 336 | border-style:solid; 337 | border-left-width:2px; 338 | border-color:rgb(0, 0, 0); 339 | background:rgba(248, 248, 255, 0); 340 | } 341 | 342 | 343 | 退出 344 | 345 | 346 | 347 | 30 348 | 30 349 | 350 | 351 | 352 | false 353 | 354 | 355 | false 356 | 357 | 358 | false 359 | 360 | 361 | false 362 | 363 | 364 | false 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/MyQSS.h: -------------------------------------------------------------------------------- 1 | #ifndef MYQSS_H 2 | #define MYQSS_H 3 | #include 4 | 5 | // 此文件统一存放一些QSS样式代码 6 | 7 | // 日期标签字体 8 | inline QString DateLabelStyle() 9 | { 10 | return 11 | "#dateLabel" 12 | "{" 13 | "font-size:30px;" 14 | "font-weight:bold;" 15 | "background:transparent;" 16 | "}"; 17 | } 18 | 19 | // 时间标签字体 20 | inline QString TimeLabelStyle() 21 | { 22 | return 23 | "#timeLabel" 24 | "{" 25 | "font-size:50px;" 26 | "font-weight:bold;" 27 | "background:transparent;" 28 | "}"; 29 | } 30 | 31 | #endif // MYQSS_H 32 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assets/Image/background-main.jpg 4 | Assets/Icon/bluetooth.png 5 | Assets/Icon/car.png 6 | Assets/Icon/map.png 7 | Assets/Icon/music.png 8 | Assets/Icon/phonecall.png 9 | Assets/Icon/settings.png 10 | Assets/Icon/weather.png 11 | 12 | 13 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Threads.cpp: -------------------------------------------------------------------------------- 1 | #include "Threads.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void Thread_DateTime::run() 7 | { 8 | while(1) 9 | { 10 | mutex.lock(); 11 | if(!threadExist) break; 12 | mutex.unlock(); 13 | 14 | // 获取当前日期 15 | QDate currentDate = QDate::currentDate(); 16 | dateL->setText(QStringLiteral("%1-%2-%3").arg(currentDate.year()).arg(currentDate.month()).arg(currentDate.day())); 17 | // 获取当前时间 18 | QTime currentTime = QTime::currentTime(); 19 | timeL->setText(currentTime.toString()); 20 | usleep(500000); // 500ms 21 | } 22 | mutex.unlock(); 23 | } 24 | void Thread_DateTime::stop() 25 | { 26 | mutex.lock(); 27 | threadExist = false; 28 | mutex.unlock(); 29 | } 30 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/Threads.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_H 2 | #define THREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // 线程:日期时间显示 11 | class Thread_DateTime : public QThread 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit Thread_DateTime(QLabel* d, QLabel* t) : dateL(d), timeL(t), threadExist(true) {} 16 | void stop(); 17 | protected: 18 | void run(); 19 | private: 20 | QLabel* dateL, * timeL; 21 | QMutex mutex; 22 | bool threadExist; 23 | }; 24 | 25 | #endif // THREAD_H 26 | -------------------------------------------------------------------------------- /x86/CarPlayMainWW/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWidget w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/down-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/down-arrow.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/duration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/duration.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/folder.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/list.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/loop.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/loop41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/loop41.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/music-playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/music-playing.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/music.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/mute.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/neaten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/neaten.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/next.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/non-music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/non-music.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/pause.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/playing.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/pre.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/quit.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/random.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/singer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/singer.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/sliderHandle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/sliderHandle.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/sweep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/sweep.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/title.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/up-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/up-arrow.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Icon/vol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Icon/vol.png -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Assets/Image/Background1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviShekdar/imx6ull_Qt5-CarPlay/19f57c0710a08ef62618775b62494329a17c8e98/x86/MusicPlayerWW/Assets/Image/Background1.jpg -------------------------------------------------------------------------------- /x86/MusicPlayerWW/LyricWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "LyricWidget.h" 2 | #include "ui_LyricWidget.h" 3 | 4 | #include 5 | #include 6 | 7 | LyricWidget::LyricWidget(QWidget *parent) : QWidget(parent), ui(new Ui::LyricWidget) 8 | { 9 | ui->setupUi(this); 10 | clear(); // 先清空完 11 | } 12 | 13 | LyricWidget::~LyricWidget() 14 | { 15 | delete ui; 16 | } 17 | 18 | bool operator <(const LyricLine& A, const LyricLine& B) 19 | { 20 | return A.time < B.time; 21 | } 22 | 23 | bool LyricWidget::process(QString filePath) 24 | { 25 | // 打开歌词文件 26 | QFile lyricFile(filePath); 27 | lyricFile.open(QFile::ReadOnly); 28 | QString content(QString::fromLocal8Bit(lyricFile.readAll())); 29 | lyricFile.close(); 30 | lines.clear(); 31 | 32 | const QRegExp rx("\\[(\\d+):(\\d+(\\.\\d+)?)\\]"); // 用来查找时间标签的正则表达式 33 | 34 | // 步骤1 35 | int pos = rx.indexIn(content); 36 | if (pos == -1) { 37 | return false; 38 | } 39 | else { 40 | int lastPos; 41 | QList timeLabels; 42 | do { 43 | // 步骤2 44 | timeLabels << (rx.cap(1).toInt() * 60 + rx.cap(2).toDouble()) * 1000; 45 | lastPos = pos + rx.matchedLength(); 46 | pos = rx.indexIn(content, lastPos); 47 | if (pos == -1) { 48 | QString text = content.mid(lastPos).trimmed(); 49 | foreach (const int& time, timeLabels) 50 | lines.push_back(LyricLine(time, text)); 51 | break; 52 | } 53 | // 步骤3 54 | QString text = content.mid(lastPos, pos - lastPos); 55 | if (!text.isEmpty()) { 56 | foreach (const int& time, timeLabels) 57 | lines.push_back(LyricLine(time, text.trimmed())); 58 | timeLabels.clear(); 59 | } 60 | } 61 | while (true); 62 | // 步骤4 63 | stable_sort(lines.begin(), lines.end()); 64 | } 65 | if (lines.size()) return true; 66 | return false; 67 | } 68 | 69 | int LyricWidget::get_index(qint64 position) 70 | { 71 | if(!lines.size()){ 72 | return -1; 73 | }else{ 74 | if(lines[0].time>=position){ 75 | return 0; 76 | } 77 | } 78 | int i=1; 79 | for(i=1;i=position){ 81 | return i-1; 82 | } 83 | } 84 | return lines.size()-1; 85 | } 86 | 87 | void LyricWidget::show(qint64 position) 88 | { 89 | int index=get_index(position); 90 | if(index==-1){ 91 | ui->lyricP3->setText(""); 92 | ui->lyricP2->setText(""); 93 | ui->lyricP1->setText(""); 94 | ui->lyricCenter->setText(u8"当前歌曲无歌词"); 95 | ui->lyricN1->setText(""); 96 | ui->lyricN2->setText(""); 97 | ui->lyricN3->setText(""); 98 | }else{ 99 | ui->lyricP3->setText(get_lyric_text(index-3)); 100 | ui->lyricP2->setText(get_lyric_text(index-2)); 101 | ui->lyricP1->setText(get_lyric_text(index-1)); 102 | ui->lyricCenter->setText(get_lyric_text(index)); 103 | ui->lyricN1->setText(get_lyric_text(index+1)); 104 | ui->lyricN2->setText(get_lyric_text(index+2)); 105 | ui->lyricN3->setText(get_lyric_text(index+3)); 106 | } 107 | } 108 | 109 | QString LyricWidget::get_lyric_text(int index) 110 | { 111 | if(index<0 || index>=lines.size()){ 112 | return ""; 113 | }else{ 114 | return lines[index].text; 115 | } 116 | } 117 | 118 | void LyricWidget::clear() 119 | { 120 | ui->lyricP3->setText(""); 121 | ui->lyricP2->setText(""); 122 | ui->lyricP1->setText(""); 123 | ui->lyricCenter->setText(""); 124 | ui->lyricN1->setText(""); 125 | ui->lyricN2->setText(""); 126 | ui->lyricN3->setText(""); 127 | } 128 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/LyricWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef LYRICWIDGET_H 2 | #define LYRICWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | // 表示一行歌词 (一个时间点 + 对应的歌词文本) 11 | class LyricLine 12 | { 13 | public: 14 | qint64 time; 15 | QString text; 16 | LyricLine(qint64 time, QString text):time(time), text(text) {} 17 | }; 18 | 19 | // 重载比较 (歌词按时间排序) 20 | bool operator <(const LyricLine& A, const LyricLine& B); 21 | 22 | namespace Ui { 23 | class LyricWidget; 24 | } 25 | 26 | class LyricWidget : public QWidget 27 | { 28 | Q_OBJECT 29 | 30 | private: 31 | vector lines; 32 | public: 33 | explicit LyricWidget(QWidget *parent = nullptr); 34 | ~LyricWidget(); 35 | 36 | // 将歌词文件的内容处理为歌词结构的QList 37 | bool process(QString filePath); 38 | 39 | // 根据时间找到对应位置的歌词 40 | int get_index(qint64 position); 41 | 42 | // 显示当前播放进度的歌词 43 | void show(qint64 position); 44 | 45 | // 根据下标获得歌词内容 46 | QString get_lyric_text(int index); 47 | 48 | // 清空歌词Label 49 | void clear(); 50 | 51 | private: 52 | Ui::LyricWidget *ui; 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/LyricWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LyricWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 385 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 0 20 | 20 21 | 600 22 | 40 23 | 24 | 25 | 26 | 27 | 11 28 | 50 29 | false 30 | 31 | 32 | 33 | color:rgb(245, 245, 245); 34 | 35 | 36 | 37 | 38 | 39 | Qt::AlignCenter 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 70 47 | 600 48 | 40 49 | 50 | 51 | 52 | 53 | 11 54 | 50 55 | false 56 | 57 | 58 | 59 | color:rgb(245, 245, 245); 60 | 61 | 62 | 63 | 64 | 65 | Qt::AlignCenter 66 | 67 | 68 | 69 | 70 | 71 | 0 72 | 120 73 | 600 74 | 40 75 | 76 | 77 | 78 | 79 | 11 80 | 50 81 | false 82 | 83 | 84 | 85 | color:rgb(245, 245, 245); 86 | 87 | 88 | 89 | 90 | 91 | Qt::AlignCenter 92 | 93 | 94 | 95 | 96 | 97 | 0 98 | 165 99 | 600 100 | 60 101 | 102 | 103 | 104 | 105 | 15 106 | 75 107 | true 108 | 109 | 110 | 111 | color:rgb(255, 255, 255); 112 | 113 | 114 | 115 | 116 | 117 | Qt::AlignCenter 118 | 119 | 120 | 121 | 122 | 123 | 0 124 | 230 125 | 600 126 | 40 127 | 128 | 129 | 130 | 131 | 11 132 | 50 133 | false 134 | 135 | 136 | 137 | color:rgb(245, 245, 245); 138 | 139 | 140 | 141 | 142 | 143 | Qt::AlignCenter 144 | 145 | 146 | 147 | 148 | 149 | 0 150 | 280 151 | 600 152 | 40 153 | 154 | 155 | 156 | 157 | 11 158 | 50 159 | false 160 | 161 | 162 | 163 | color:rgb(245, 245, 245); 164 | 165 | 166 | 167 | 168 | 169 | Qt::AlignCenter 170 | 171 | 172 | 173 | 174 | 175 | 0 176 | 330 177 | 600 178 | 40 179 | 180 | 181 | 182 | 183 | 11 184 | 50 185 | false 186 | 187 | 188 | 189 | color:rgb(245, 245, 245); 190 | 191 | 192 | 193 | 194 | 195 | Qt::AlignCenter 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MainWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ui_MainWidget.h" 9 | #include "MusicListWidget.h" 10 | #include 11 | #include "MyQSS.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // 设置全局变量 18 | QStringList nameFile; 19 | QString openDir; 20 | int preNum = 0, volRecord = 50; 21 | 22 | MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget) 23 | { 24 | ui->setupUi(this); 25 | 26 | init_ui(); 27 | init_play(); 28 | init_sqlite(); 29 | init_musiclist_widget(); 30 | init_shm(); 31 | 32 | // scan_music(); 33 | // add_music_to_list(nameFile); 34 | // init_playlist(); 35 | 36 | // QUrl baseUrl("file:///D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/孙燕姿-极美.mp3"); // "file:///"不可少 37 | // //QUrl relativeUrl("孙燕姿-极美.mp3"); 38 | // //qDebug() << "Url:" << baseUrl.resolved(relativeUrl) << "\n"; 39 | // MyMusic msc(baseUrl); // resolved:拼接绝对变量和相对变量 40 | // qDebug() << "Author:" << msc.author 41 | // << "Title:" << msc.title 42 | // << "AlbumTitle:" << msc.albumTitle 43 | // << "Duration:" << msc.duration 44 | // << "BitRate:" << msc.bitRate; 45 | } 46 | 47 | MainWidget::~MainWidget() 48 | { 49 | delete ui; 50 | } 51 | 52 | extern QString my_format_time(qint64 msTime); // 外部链接已定义好的函数 53 | 54 | void MainWidget::init_ui() 55 | { 56 | setFixedSize(1024, 600); 57 | setAttribute(Qt::WA_StyledBackground); // 告诉窗口,应该使用设置的样式表进行渲染控件背景 58 | setWindowFlags(Qt::FramelessWindowHint); // 去除标题栏 59 | ui->sliderVol->hide(); 60 | ui->stackedWidget->setCurrentIndex(0); 61 | ui->musicListWidget->set_icon(QIcon(":/Assets/Icon/music.png")); 62 | // 封面图片 63 | QPixmap albumImg(":/Assets/Icon/non-music.png"); 64 | ui->coverPic->setPixmap(albumImg); 65 | ui->coverPic->setScaledContents(true); 66 | // 需要图标变换的图标初始化 67 | ui->btnPlay->setStyleSheet(PauseStyle()); 68 | } 69 | 70 | void MainWidget::init_play() 71 | { 72 | //播放器初始化 73 | player= new QMediaPlayer(this); 74 | playlist=new QMediaPlaylist; 75 | playlist->setPlaybackMode(QMediaPlaylist::Loop); 76 | player->setPlaylist(playlist); 77 | player->setVolume(volRecord); 78 | // 连接播放有关的信号与槽 79 | connect(player, &QMediaPlayer::stateChanged, this, &MainWidget::update_btn_play); // 播放状态改变 80 | connect(ui->sliderProgress, &QAbstractSlider::valueChanged, this, &MainWidget::set_position); // 滑槽拖动带动播放跳转 81 | connect(player, &QMediaPlayer::positionChanged, this, &MainWidget::update_duration); // 歌曲播放时间变化 82 | connect(player, &QMediaPlayer::durationChanged, this, &MainWidget::update_position); // 滑槽位置随播放进度变化而变化 83 | connect(player, &QMediaPlayer::metaDataAvailableChanged, this, &MainWidget::update_info); // 切歌更换元数据 84 | } 85 | 86 | void MainWidget::init_sqlite() 87 | { 88 | QSqlDatabase database; 89 | if(QSqlDatabase::contains("qt_sql_default_connection")) // 检查是否建立过连接了 90 | { 91 | database = QSqlDatabase::database("qt_sql_default_connection"); 92 | } 93 | else 94 | { 95 | database = QSqlDatabase::addDatabase("QSQLITE"); 96 | database.setDatabaseName("Music.db"); 97 | database.setUserName("WW"); 98 | database.setPassword("123456"); 99 | qDebug() << "数据库建立连接"; // 测试用 100 | if(!database.open()) 101 | { 102 | QMessageBox::critical(this,"无法打开数据库文件:Music.db",database.lastError().databaseText()); 103 | exit(-1); 104 | } 105 | } 106 | // 删除表,测试用 107 | // QSqlQuery querySql; 108 | // QString deleteTable = "drop table MusicInfo"; 109 | // querySql.prepare(deleteTable); 110 | // if(querySql.exec()) qDebug() << "删除表"; 111 | // 检查表是否存在,不存在就创建 112 | QSqlQuery querySql; 113 | querySql.exec(QString("select count(*) from sqlite_master where type='table' and name='%1'").arg("MusicInfo")); 114 | if(querySql.next()) 115 | { 116 | if(querySql.value(0).toInt() == 0) 117 | { 118 | QSqlQuery query; 119 | QString createSql = "create table MusicInfo (url varchar(200), author varchar(50), title varchar(50), duration bigint, albumTitle varchar(50), bitRate int)"; 120 | query.prepare(createSql); 121 | if(query.exec()) qDebug() << "表建立"; // 测试用 122 | } 123 | } 124 | } 125 | 126 | void MainWidget::init_musiclist_widget() 127 | { 128 | ui->musicListWidget->clear(); // 先清空 129 | ui->musicListWidget->setFrameShape(QListWidget::NoFrame); // 去除边框 130 | ui->musicListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 131 | ui->musicListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 132 | // 设置字体 133 | QFont font; 134 | font.setPointSize(14); 135 | font.setBold(true); 136 | ui->musicListWidget->setFont(font); 137 | ui->musicListWidget->setIconSize(QSize(30, 30)); 138 | ui->musicListWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel); // 设置为像素滚动 139 | QScroller::grabGesture(ui->musicListWidget, QScroller::LeftMouseButtonGesture); // 设置鼠标左键拖动 140 | // 从数据库恢复 141 | ui->musicListWidget->musicList.read_from_sql(); 142 | ui->musicListWidget->refresh(); 143 | } 144 | 145 | void MainWidget::shm_to_zero() 146 | { 147 | // 开始写 148 | shmMenuToMusic.lock(); 149 | int* sharedData = static_cast(shmMenuToMusic.data()); 150 | *sharedData = 0; 151 | shmMenuToMusic.unlock(); 152 | shmMenuToMusic.detach(); 153 | } 154 | 155 | void MainWidget::exec_by_num(int num) 156 | { 157 | switch(num) 158 | { 159 | case SWITCH_VISIBILITY: 160 | this->show(); 161 | shm_to_zero(); 162 | break; 163 | case VOL_UP: 164 | ui->sliderVol->setValue(volRecord); 165 | shm_to_zero(); 166 | break; 167 | case VOL_DOWN: 168 | if(ui->sliderVol->value() > 20) ui->sliderVol->setValue(20); 169 | volRecord = ui->sliderVol->value(); 170 | shm_to_zero(); 171 | break; 172 | case MUSIC_PLAY: 173 | player->play(); 174 | shm_to_zero(); 175 | break; 176 | case MUSIC_PAUSE: 177 | player->pause(); 178 | shm_to_zero(); 179 | break; 180 | case CLOSE_APP: 181 | this->close(); 182 | shm_to_zero(); 183 | break; 184 | default: 185 | break; 186 | } 187 | } 188 | 189 | void MainWidget::init_shm() 190 | { 191 | shmMenuToMusic.setKey(QStringLiteral("shm_for_music")); // 音乐播放器用共享内存键值 192 | shmMusicToMenu.setKey(QStringLiteral("shm_for_menu")); // 菜单用共享内存键值 193 | switchAppTimer = new QTimer(this); 194 | detachTimer = new QTimer(this); 195 | // 定时监测共享内存并读数据 196 | readTimer = new QTimer(this); 197 | connect(readTimer, &QTimer::timeout, [&]() 198 | { 199 | qDebug() << "等待"; 200 | if(shmMenuToMusic.attach()) 201 | { 202 | int* sharedData = static_cast(shmMenuToMusic.data()); 203 | int currentNum = *sharedData; 204 | qDebug() << "MusicPlayer:" << currentNum; 205 | exec_by_num(currentNum); 206 | shmMenuToMusic.detach(); 207 | } 208 | }); 209 | readTimer->start(1000); 210 | } 211 | 212 | void MainWidget::update_btn_play() 213 | { 214 | if(player->state() == QMediaPlayer::PlayingState) 215 | ui->btnPlay->setStyleSheet(PlayingStyle()); 216 | else ui->btnPlay->setStyleSheet(PauseStyle()); 217 | } 218 | 219 | void MainWidget::set_position(int pos) 220 | { 221 | if (qAbs(player->position() - pos) > 99) 222 | player->setPosition(pos); 223 | } 224 | 225 | void MainWidget::update_duration(qint64 pos) 226 | { 227 | ui->sliderProgress->setValue(static_cast(pos)); 228 | ui->durationLabel->setText(my_format_time(pos) + "/" + my_format_time(player->duration())); 229 | if(playlist->currentIndex()>=0)ui->lyricWidget->show(pos); 230 | } 231 | 232 | void MainWidget::update_position(qint64 duration) 233 | { 234 | ui->sliderProgress->setRange(0, static_cast(duration)); 235 | ui->sliderProgress->setEnabled(static_cast(duration) > 0); 236 | if(!(static_cast(duration) > 0)) 237 | { 238 | //无音乐播放时,界面元素 239 | QImage image(":/Assets/Icon/non-music.png"); 240 | ui->coverPic->setPixmap(QPixmap::fromImage(image)); 241 | ui->musicTitleLabel->setText(u8"暂无歌曲"); 242 | ui->musicAlbumLabel->setText(u8""); 243 | ui->musicAuthorLabel->setText(u8""); 244 | ui->lyricWidget->clear(); 245 | } 246 | ui->sliderProgress->setPageStep(static_cast(duration) / 10); 247 | } 248 | 249 | void MainWidget::update_info(bool available) 250 | { 251 | if (available) 252 | { 253 | // 返回可用MP3元数据列表(调试时可以查看) 254 | // QStringList listInfo_debug = player->availableMetaData(); 255 | // qDebug() << listInfo_debug; 256 | 257 | QString info=""; 258 | QString author = player->metaData(QStringLiteral("Author")).toStringList().join(","); 259 | info.append(author); 260 | QString title = player->metaData(QStringLiteral("Title")).toString(); 261 | QString albumTitle = player->metaData(QStringLiteral("AlbumTitle")).toString(); 262 | info.append(" - "+title); 263 | info.append(" ["+my_format_time(player->duration())+"]"); 264 | 265 | //封面图片(应获取"ThumbnailImage" From: https://www.zhihu.com/question/36859497) 266 | QImage picImage = player->metaData(QStringLiteral("ThumbnailImage")).value(); 267 | if(picImage.isNull()) picImage = QImage(":/Assets/Icon/non-music.png"); 268 | ui->coverPic->setPixmap(QPixmap::fromImage(picImage)); 269 | ui->coverPic->setScaledContents(true); 270 | //改变正在播放歌曲的图标 271 | for(int i = 0; i < playlist->mediaCount(); i++) 272 | { 273 | QListWidgetItem *p = ui->musicListWidget->item(i); 274 | p->setIcon(ui->musicListWidget->get_icon()); 275 | } 276 | int index = playlist->currentIndex(); 277 | QListWidgetItem *p = ui->musicListWidget->item(index); 278 | p->setIcon(QIcon(":/Assets/Icon/music-playing.png")); 279 | 280 | // 歌词界面显示的信息 281 | ui->musicTitleLabel->setText(title); 282 | ui->musicAlbumLabel->setText(u8"专辑:"+albumTitle); 283 | ui->musicAuthorLabel->setText(u8"歌手:"+author); 284 | 285 | // 解析歌词 286 | ui->lyricWidget->process(ui->musicListWidget->musicList.musicArr[index].get_lyric()); 287 | } 288 | } 289 | 290 | void MainWidget::scan_music() 291 | { 292 | openDir = QCoreApplication::applicationDirPath() + "/Music"; 293 | QDir dirList(openDir); 294 | qDebug() << openDir; 295 | nameFile << "*.mp3" << "*.flac"; // 选择要过滤的文件格式 296 | nameFile = dirList.entryList(nameFile, QDir::Files | QDir::Readable, QDir::Name); 297 | qDebug() << "nameFile =" << nameFile << "nameFileSize =" << nameFile.size(); 298 | } 299 | 300 | void MainWidget::add_music_to_list(const QStringList& fileNames) 301 | { 302 | QListWidgetItem* item = NULL; 303 | for(int i = 0; i < fileNames.size(); i++) 304 | { 305 | ui->musicListWidget->addItem(fileNames[i]); 306 | item = ui->musicListWidget->item(i); 307 | item->setForeground(Qt::black); 308 | item->setSizeHint(QSize((float)600/1024*this->geometry().width(), (float)50/600*this->geometry().height())); 309 | item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); 310 | item->setIcon(QIcon(":/Assets/Icon/music.png")); 311 | } 312 | } 313 | 314 | void MainWidget::zoom(QPushButton* btn) 315 | { 316 | QPropertyAnimation* animation = new QPropertyAnimation(btn,"geometry"); 317 | //设置动画时间间隔 318 | animation->setDuration(400); 319 | //设置起始位置 320 | animation->setStartValue(QRect(btn->x(), btn->y()+4, btn->width(), btn->height())); 321 | //设置结束位置 322 | animation->setEndValue(QRect(btn->x(), btn->y(), btn->width(), btn->height())); 323 | //设置弹跳曲线 324 | animation->setEasingCurve(QEasingCurve::OutBounce); 325 | //执行动画 326 | animation->start(); 327 | } 328 | 329 | void MainWidget::on_btnVol_clicked() 330 | { 331 | zoom(ui->btnVol); 332 | if(ui->sliderVol->isVisible()) ui->sliderVol->setVisible(false); 333 | else ui->sliderVol->setVisible(true); 334 | } 335 | 336 | void MainWidget::on_btnQuit_clicked() 337 | { 338 | zoom(ui->btnQuit); 339 | 340 | // 向菜单的共享内存写命令 341 | if(shmMusicToMenu.isAttached()) shmMusicToMenu.detach(); 342 | if(!shmMusicToMenu.create(sizeof(int), QSharedMemory::ReadWrite)) 343 | { 344 | qDebug() << "Failed to create shared memory!"; 345 | return; 346 | } 347 | // 开始写 348 | shmMusicToMenu.lock(); 349 | int* sharedData = static_cast(shmMusicToMenu.data()); 350 | *sharedData = SWITCH_VISIBILITY; 351 | shmMusicToMenu.unlock(); 352 | 353 | // 延时detach内存 354 | detachTimer->start(1000); 355 | connect(detachTimer, &QTimer::timeout, [&]() 356 | { 357 | detachTimer->stop(); 358 | shmMusicToMenu.detach(); 359 | }); 360 | 361 | // 延时隐藏进程 362 | this->hide(); 363 | //close(); 364 | } 365 | 366 | void MainWidget::on_btnList_clicked() 367 | { 368 | zoom(ui->btnList); 369 | if(ui->stackedWidget->currentIndex() != 1) ui->stackedWidget->setCurrentIndex(1); 370 | else ui->stackedWidget->setCurrentIndex(0); 371 | } 372 | 373 | void MainWidget::on_btnPlay_clicked() 374 | { 375 | zoom(ui->btnPlay); 376 | if(player->state() == QMediaPlayer::PlayingState) 377 | { 378 | player->pause(); 379 | } 380 | else if(player->state() == QMediaPlayer::PausedState){ 381 | 382 | player->play(); 383 | } 384 | // 歌单里有歌但是是停止状态时 385 | else if((!playlist->isEmpty()) && (player->state() == QMediaPlayer::StoppedState)) 386 | { 387 | playlist->setCurrentIndex(0); 388 | player->play(); 389 | } 390 | else if(playlist->isEmpty()) return; 391 | } 392 | 393 | void MainWidget::on_btnPre_clicked() 394 | { 395 | zoom(ui->btnPre); 396 | playlist->previous(); 397 | if(!playlist->isEmpty()) player->play(); 398 | } 399 | 400 | void MainWidget::on_btnNext_clicked() 401 | { 402 | zoom(ui->btnNext); 403 | playlist->next(); 404 | if(!playlist->isEmpty()) player->play(); 405 | } 406 | 407 | void MainWidget::init_playlist() 408 | { 409 | playlist->clear(); // 先清空 410 | for(int i = 0; i < nameFile.size(); i++) 411 | { 412 | playlist->addMedia(QUrl(openDir + "/" + nameFile[i])); 413 | } 414 | } 415 | 416 | void MainWidget::on_sliderVol_valueChanged(int value) 417 | { 418 | player->setVolume(value); 419 | if(value <= 0) ui->btnVol->setStyleSheet(MuteStyle()); 420 | else ui->btnVol->setStyleSheet(VolStyle()); 421 | } 422 | 423 | void MainWidget::on_btnPlayMode_clicked() 424 | { 425 | zoom(ui->btnPlayMode); 426 | 427 | if(playlist->playbackMode() == QMediaPlaylist::Loop) 428 | { 429 | ui->btnPlayMode->setStyleSheet(RandomStyle()); 430 | playlist->setPlaybackMode(QMediaPlaylist::Random); 431 | } 432 | else if(playlist->playbackMode() == QMediaPlaylist::Random) 433 | { 434 | ui->btnPlayMode->setStyleSheet(Loop41Style()); 435 | playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); 436 | } 437 | else if(playlist->playbackMode() == QMediaPlaylist::CurrentItemInLoop) 438 | { 439 | ui->btnPlayMode->setStyleSheet(LoopStyle()); 440 | playlist->setPlaybackMode(QMediaPlaylist::Loop); 441 | } 442 | } 443 | 444 | void MainWidget::on_btnAdd_clicked() 445 | { 446 | zoom(ui->btnAdd); 447 | QFileDialog fileDialog(this); 448 | fileDialog.setAcceptMode(QFileDialog::AcceptOpen); // 设置fileDialog用来打开文件 449 | fileDialog.setFileMode(QFileDialog::ExistingFiles); 450 | fileDialog.setWindowTitle(u8"添加本地音乐(注:自动过滤,按下\"Ctrl+A\"全选添加即可;不支持添加文件夹)"); 451 | QStringList list;list<<"application/octet-stream"; // 字节流 452 | fileDialog.setMimeTypeFilters(list); 453 | qDebug() << openDir; 454 | fileDialog.setDirectory(openDir); // 设置默认指定到文件夹 455 | if (fileDialog.exec() == QDialog::Accepted) 456 | { 457 | QList urls=fileDialog.selectedUrls(); 458 | ui->musicListWidget->musicList.add_music(urls); 459 | ui->musicListWidget->refresh(); 460 | ui->stackedWidget->setCurrentIndex(1); // 切换到歌单列表 461 | } 462 | } 463 | 464 | void MainWidget::on_btnNeaten_clicked() 465 | { 466 | zoom(ui->btnNeaten); 467 | ui->musicListWidget->musicList.neaten(); 468 | ui->musicListWidget->refresh(); 469 | } 470 | 471 | void MainWidget::on_musicListWidget_clicked(const QModelIndex &index) 472 | { 473 | playlist->clear(); 474 | ui->musicListWidget->musicList.add_to_playlist(playlist); 475 | int i = index.row(); 476 | playlist->setCurrentIndex(i); 477 | player->play(); 478 | } 479 | 480 | void MainWidget::on_btnSortSinger_clicked() 481 | { 482 | zoom(ui->btnSortSinger); 483 | ui->musicListWidget->musicList.sort_by(AUTHOR); 484 | ui->musicListWidget->refresh(); 485 | } 486 | 487 | void MainWidget::on_btnSortTitle_clicked() 488 | { 489 | zoom(ui->btnSortTitle); 490 | ui->musicListWidget->musicList.sort_by(TITLE); 491 | ui->musicListWidget->refresh(); 492 | } 493 | 494 | void MainWidget::on_btnSortDuration_clicked() 495 | { 496 | zoom(ui->btnSortDuration); 497 | ui->musicListWidget->musicList.sort_by(DURATION); 498 | ui->musicListWidget->refresh(); 499 | } 500 | 501 | void MainWidget::on_btnSweep_clicked() 502 | { 503 | ui->musicListWidget->musicList.clear(); 504 | ui->musicListWidget->refresh(); 505 | } 506 | 507 | // 测试程序1:打开音乐url看看能否成功打开、有没有可用的元数据 508 | // 学到了QUrl绝对路径和相对路径该怎么表示 509 | //QUrl baseUrl("file:///D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/"); // "file:///"不可少 510 | //QUrl relativeUrl("林俊杰-进阶.mp3"); 511 | //Music msc(baseUrl.resolved(relativeUrl)); // resolved:拼接绝对变量和相对变量 512 | //Music msc(QUrl::fromLocalFile("D:/Software/BaiduNetdisk/BaiduSyncdisk/Code/QtCode/0_CarPlaySys/MusicPlayer/MusicPlayerWW/Assets/Music/林俊杰-进阶.mp3")); // 用这样表示绝对路径也可以 513 | 514 | // 测试程序2:选择指定文件夹并筛选出指定音频文件 515 | // QCoreApplication::applicationDirPath() 为获取应用程序运行路径 516 | // QFileDialog fileDialog; 517 | // QString openDir = QCoreApplication::applicationDirPath() + "/Music"; 518 | // QDir dirList(openDir); 519 | // QStringList nameFile; 520 | // nameFile << "*.mp3" << "*.flac"; // 选择要过滤的文件格式 521 | // nameFile = dirList.entryList(nameFile, QDir::Files | QDir::Readable, QDir::Name); 522 | // qDebug() << "nameFiles == " << nameFile; 523 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MainWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "MyMusic.h" 14 | #include "MusicList.h" 15 | 16 | // 共享内存传输的一些命令 17 | #define SWITCH_VISIBILITY 1 // 改变窗体可见性 18 | #define VOL_DOWN 2 // 音量减小 19 | #define VOL_UP 3 // 音量增大 20 | #define MUSIC_PLAY 4 // 播放音乐 21 | #define MUSIC_PAUSE 5 // 暂停音乐 22 | #define CLOSE_APP 6 // 关闭APP 23 | 24 | QT_BEGIN_NAMESPACE 25 | namespace Ui { class MainWidget; } 26 | QT_END_NAMESPACE 27 | 28 | class MainWidget : public QWidget 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | MainWidget(QWidget *parent = nullptr); 34 | ~MainWidget(); 35 | // 初始化 36 | void init_ui(); // 初始化UI和按钮图标 37 | 38 | void init_play(); // 初始化播放器 39 | void init_sqlite(); // 初始化数据库sqlite 40 | void init_musiclist_widget(); // 初始化MusicListWidget的格式 41 | 42 | void init_shm(); 43 | void exec_by_num(int num); // 根据共享内存传来的命令,执行相应的任务 44 | void shm_to_zero(); // 给共享内存置0 45 | 46 | void update_btn_play(); // 切换播放按钮的QSS 47 | void set_position(int pos); 48 | void update_position(qint64 duration); 49 | void update_duration(qint64 pos); 50 | void update_info(bool available); 51 | 52 | // 自动扫描歌曲并添加到列表所用到的3种方法,暂时不用 53 | void init_playlist(); 54 | void scan_music(); 55 | void add_music_to_list(const QStringList& fileNames); 56 | 57 | void zoom(QPushButton* btn); // 按钮动画 58 | 59 | private slots: 60 | void on_btnVol_clicked(); 61 | 62 | void on_btnQuit_clicked(); 63 | 64 | void on_btnList_clicked(); 65 | 66 | void on_btnPlay_clicked(); 67 | 68 | void on_btnPre_clicked(); 69 | 70 | void on_btnNext_clicked(); 71 | 72 | void on_sliderVol_valueChanged(int value); 73 | 74 | void on_btnPlayMode_clicked(); 75 | 76 | void on_btnAdd_clicked(); 77 | 78 | void on_btnNeaten_clicked(); 79 | 80 | void on_musicListWidget_clicked(const QModelIndex &index); 81 | 82 | void on_btnSortSinger_clicked(); 83 | 84 | void on_btnSortTitle_clicked(); 85 | 86 | void on_btnSortDuration_clicked(); 87 | 88 | void on_btnSweep_clicked(); 89 | 90 | private: 91 | Ui::MainWidget *ui; 92 | 93 | QMediaPlayer* player; 94 | QMediaPlaylist* playlist; 95 | QTimer* switchAppTimer, * readTimer, * detachTimer; 96 | QSharedMemory shmMenuToMusic, shmMusicToMenu; 97 | }; 98 | #endif 99 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MainWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 600 11 | 12 | 13 | 14 | MainWidget 15 | 16 | 17 | #MainWidget{ 18 | border-image:url(:/Assets/Image/Background1.jpg) 19 | } 20 | 21 | 22 | 23 | 24 | 0 25 | 520 26 | 1024 27 | 80 28 | 29 | 30 | 31 | #frame 32 | { 33 | background:rgb(248,248,255); 34 | border-top-style:solid; 35 | border-top-width:1px; 36 | border-top-color:rgba(220, 220, 220, 1); 37 | } 38 | 39 | 40 | QFrame::StyledPanel 41 | 42 | 43 | QFrame::Raised 44 | 45 | 46 | 47 | 48 | 20 49 | 20 50 | 40 51 | 40 52 | 53 | 54 | 55 | #btnPre 56 | { 57 | image: url(:/Assets/Icon/pre.png); 58 | background-color:transparent; 59 | border:transparent; 60 | } 61 | 62 | 63 | 64 | 65 | 66 | 67 | 40 68 | 40 69 | 70 | 71 | 72 | false 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | 160 85 | 20 86 | 40 87 | 40 88 | 89 | 90 | 91 | #btnNext 92 | { 93 | image: url(:/Assets/Icon/next.png); 94 | background-color:transparent; 95 | border:transparent; 96 | } 97 | 98 | 99 | 100 | 101 | 102 | 103 | 40 104 | 40 105 | 106 | 107 | 108 | false 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | 118 | 119 | 120 | 85 121 | 15 122 | 50 123 | 50 124 | 125 | 126 | 127 | #btnPlay 128 | { 129 | image:url(:/Assets/Icon/pause.png); 130 | background-repeat:no-repeat; 131 | background-color:transparent; 132 | position:center center; 133 | border:transparent; 134 | } 135 | 136 | 137 | 138 | 139 | 140 | 141 | 40 142 | 40 143 | 144 | 145 | 146 | false 147 | 148 | 149 | true 150 | 151 | 152 | false 153 | 154 | 155 | 156 | 157 | 158 | 814 159 | 20 160 | 40 161 | 40 162 | 163 | 164 | 165 | #btnPlayMode 166 | { 167 | image:url(:/Assets/Icon/loop.png); 168 | background-repeat:no-repeat; 169 | background-color:transparent; 170 | background-position:center center; 171 | border:transparent; 172 | } 173 | 174 | 175 | 176 | 177 | 178 | 179 | 40 180 | 40 181 | 182 | 183 | 184 | false 185 | 186 | 187 | true 188 | 189 | 190 | false 191 | 192 | 193 | 194 | 195 | 196 | 235 197 | 20 198 | 400 199 | 40 200 | 201 | 202 | 203 | #sliderProgress 204 | { 205 | background:transparent; 206 | border:15px solid rgb(248,248,255); 207 | } 208 | QSlider::add-page:Horizontal 209 | { 210 | background-color: rgb(205,201,201); 211 | 212 | } 213 | QSlider::sub-page:Horizontal 214 | { 215 | background-color: rgb(255, 20, 147); 216 | } 217 | QSlider::groove:Horizontal 218 | { 219 | background:transparent; 220 | height:6px; 221 | } 222 | QSlider::handle:Horizontal 223 | { 224 | height: 18px; 225 | width: 18px; 226 | margin: -6px; 227 | border-image:url(:/Assets/Icon/sliderHandle.png); 228 | } 229 | 230 | 231 | 100 232 | 233 | 234 | 0 235 | 236 | 237 | Qt::Horizontal 238 | 239 | 240 | 241 | 242 | 243 | 963 244 | 19 245 | 42 246 | 42 247 | 248 | 249 | 250 | #btnList 251 | { 252 | image: url(:/Assets/Icon/list.png); 253 | background-color:transparent; 254 | border:transparent; 255 | } 256 | 257 | 258 | 259 | 260 | 261 | 262 | 40 263 | 40 264 | 265 | 266 | 267 | false 268 | 269 | 270 | true 271 | 272 | 273 | false 274 | 275 | 276 | 277 | 278 | 279 | 893 280 | 24 281 | 32 282 | 32 283 | 284 | 285 | 286 | #btnVol 287 | { 288 | image: url(:/Assets/Icon/vol.png); 289 | background-color:transparent; 290 | border:transparent; 291 | } 292 | 293 | 294 | 295 | 296 | 297 | 298 | 40 299 | 40 300 | 301 | 302 | 303 | false 304 | 305 | 306 | true 307 | 308 | 309 | false 310 | 311 | 312 | 313 | 314 | 315 | 640 316 | 25 317 | 140 318 | 30 319 | 320 | 321 | 322 | 323 | Dubai 324 | 12 325 | 326 | 327 | 328 | Qt::LeftToRight 329 | 330 | 331 | 00:00/00:00 332 | 333 | 334 | Qt::AlignCenter 335 | 336 | 337 | 338 | 339 | 340 | 341 | 0 342 | 0 343 | 1024 344 | 520 345 | 346 | 347 | 348 | 1 349 | 350 | 351 | 352 | 353 | 354 | 80 355 | 80 356 | 240 357 | 240 358 | 359 | 360 | 361 | #coverPic 362 | { 363 | image:url(":/Assets/Icon/non-music.png"); 364 | background-color:transparent; 365 | } 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 20 375 | 340 376 | 360 377 | 40 378 | 379 | 380 | 381 | 382 | 14 383 | 75 384 | true 385 | 386 | 387 | 388 | color:rgb(248, 248, 255); 389 | 390 | 391 | 392 | 393 | 394 | Qt::AlignCenter 395 | 396 | 397 | 398 | 399 | 400 | 20 401 | 395 402 | 360 403 | 30 404 | 405 | 406 | 407 | 408 | 10 409 | 75 410 | true 411 | 412 | 413 | 414 | color:rgb(248, 248, 255); 415 | 416 | 417 | 418 | 419 | 420 | Qt::AlignCenter 421 | 422 | 423 | 424 | 425 | 426 | 20 427 | 435 428 | 360 429 | 30 430 | 431 | 432 | 433 | 434 | 10 435 | 75 436 | true 437 | 438 | 439 | 440 | color:rgb(248, 248, 255); 441 | 442 | 443 | 444 | 445 | 446 | Qt::AlignCenter 447 | 448 | 449 | 450 | 451 | 452 | 400 453 | 80 454 | 600 455 | 385 456 | 457 | 458 | 459 | 460 | 461 | 462 | #pageMusicList 463 | { 464 | background-color: rgb(248, 248, 255); 465 | } 466 | 467 | 468 | 469 | 470 | 0 471 | 55 472 | 1024 473 | 465 474 | 475 | 476 | 477 | #musicListWidget 478 | { 479 | background-color:rgba(220, 220, 220, 0.2); 480 | outline:none; 481 | border-image:transparent; 482 | } 483 | QListWidget::item:selected:active 484 | { 485 | background:rgb(255, 20, 147); 486 | } 487 | 488 | 489 | 490 | 491 | 492 | 170 493 | 9 494 | 36 495 | 36 496 | 497 | 498 | 499 | #btnSortSinger 500 | { 501 | image: url(:/Assets/Icon/singer.png); 502 | background-color:transparent; 503 | border:transparent; 504 | } 505 | 506 | 507 | 508 | 509 | 510 | 511 | 40 512 | 40 513 | 514 | 515 | 516 | false 517 | 518 | 519 | true 520 | 521 | 522 | false 523 | 524 | 525 | 526 | 527 | 528 | 231 529 | 7 530 | 40 531 | 40 532 | 533 | 534 | 535 | #btnSortTitle 536 | { 537 | image: url(:/Assets/Icon/title.png); 538 | background-color:transparent; 539 | border:transparent; 540 | } 541 | 542 | 543 | 544 | 545 | 546 | 547 | 40 548 | 40 549 | 550 | 551 | 552 | false 553 | 554 | 555 | true 556 | 557 | 558 | false 559 | 560 | 561 | 562 | 563 | 564 | 296 565 | 11 566 | 32 567 | 32 568 | 569 | 570 | 571 | #btnSortDuration 572 | { 573 | image: url(:/Assets/Icon/duration.png); 574 | background-color:transparent; 575 | border:transparent; 576 | } 577 | 578 | 579 | 580 | 581 | 582 | 583 | 40 584 | 40 585 | 586 | 587 | 588 | false 589 | 590 | 591 | true 592 | 593 | 594 | false 595 | 596 | 597 | 598 | 599 | 600 | 20 601 | 7 602 | 40 603 | 40 604 | 605 | 606 | 607 | #btnAdd 608 | { 609 | image: url(:/Assets/Icon/folder.png); 610 | background-color:transparent; 611 | border:transparent; 612 | } 613 | 614 | 615 | 616 | 617 | 618 | 619 | 40 620 | 40 621 | 622 | 623 | 624 | false 625 | 626 | 627 | true 628 | 629 | 630 | false 631 | 632 | 633 | 634 | 635 | 636 | 85 637 | 7 638 | 40 639 | 40 640 | 641 | 642 | 643 | #btnNeaten 644 | { 645 | image: url(:/Assets/Icon/neaten.png); 646 | background-color:transparent; 647 | border:transparent; 648 | } 649 | 650 | 651 | 652 | 653 | 654 | 655 | 40 656 | 40 657 | 658 | 659 | 660 | false 661 | 662 | 663 | true 664 | 665 | 666 | false 667 | 668 | 669 | 670 | 671 | 672 | 358 673 | 7 674 | 40 675 | 40 676 | 677 | 678 | 679 | #btnSweep 680 | { 681 | image: url(:/Assets/Icon/sweep.png); 682 | background-color:transparent; 683 | border:transparent; 684 | } 685 | 686 | 687 | 688 | 689 | 690 | 691 | 40 692 | 40 693 | 694 | 695 | 696 | false 697 | 698 | 699 | true 700 | 701 | 702 | false 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 884 711 | 320 712 | 50 713 | 200 714 | 715 | 716 | 717 | #sliderVol 718 | { 719 | background-color:rgb(248,248,255); 720 | min-width:20px; 721 | max-width:20px; 722 | border:15px solid rgb(248,248,255); 723 | } 724 | QSlider::add-page:vertical 725 | { 726 | background-color: rgb(255, 20, 147); 727 | } 728 | QSlider::sub-page:vertical 729 | { 730 | background-color: rgb(205,201,201); 731 | } 732 | QSlider::groove:vertical 733 | { 734 | background:transparent; 735 | width:8px; 736 | } 737 | QSlider::handle:vertical 738 | { 739 | height: 18px; 740 | width: 18px; 741 | margin: -5px; 742 | border-image:url(:/Assets/Icon/sliderHandle.png); 743 | } 744 | 745 | 746 | 100 747 | 748 | 749 | 50 750 | 751 | 752 | Qt::Vertical 753 | 754 | 755 | 756 | 757 | 758 | 984 759 | 15 760 | 25 761 | 25 762 | 763 | 764 | 765 | Qt::DefaultContextMenu 766 | 767 | 768 | #btnQuit 769 | { 770 | image: url(:/Assets/Icon/quit.png); 771 | background-color:transparent; 772 | border:transparent; 773 | } 774 | 775 | 776 | 777 | 778 | 779 | 780 | 20 781 | 20 782 | 783 | 784 | 785 | false 786 | 787 | 788 | false 789 | 790 | 791 | false 792 | 793 | 794 | false 795 | 796 | 797 | frame 798 | stackedWidget 799 | btnQuit 800 | sliderVol 801 | 802 | 803 | 804 | MusicListWidget 805 | QListWidget 806 |
musiclistwidget.h
807 |
808 | 809 | LyricWidget 810 | QWidget 811 |
lyricwidget.h
812 | 1 813 |
814 |
815 | 816 | 817 |
818 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MusicList.cpp: -------------------------------------------------------------------------------- 1 | #include "MusicList.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "MusicListWidget.h" 7 | #include 8 | #include 9 | #include 10 | 11 | MusicList::MusicList(const QList &urls) 12 | { 13 | add_music(urls); 14 | } 15 | 16 | void MusicList::add_music(const QList &urls) 17 | { 18 | //实测这里耗时较长,所以添加一个进度显示对话框 19 | QProgressDialog proDialog(u8"添加进度",u8"取消",0,urls.size()); 20 | proDialog.setMinimumSize(350,150); 21 | proDialog.setWindowModality(Qt::WindowModal); 22 | proDialog.setWindowTitle("添加中...请稍后"); 23 | proDialog.show(); 24 | int x=0; 25 | foreach (QUrl i, urls) { 26 | x++; 27 | proDialog.setValue(x); 28 | //过滤Url的类型 29 | QMimeDatabase db; 30 | QMimeType mime = db.mimeTypeForFile(i.toLocalFile()); 31 | if(mime.name()!="audio/mpeg" && mime.name()!="audio/flac") 32 | { 33 | continue; 34 | } 35 | //剩下的符合类型 36 | musicArr.push_back(MyMusic(i)); 37 | musicArr[musicArr.size() - 1].insert_sql(); // 放入数据库 38 | if(proDialog.wasCanceled()) break; 39 | } 40 | } 41 | 42 | void MusicList::add_music(const MyMusic &iMusic) 43 | { 44 | musicArr.push_back(iMusic); 45 | musicArr[musicArr.size() - 1].insert_sql(); // 放入数据库 46 | } 47 | 48 | MyMusic MusicList::get_music(int pos) 49 | { 50 | return musicArr[pos]; 51 | } 52 | 53 | void MusicList::add_to_playlist(QMediaPlaylist* playlist) 54 | { 55 | for(auto i=musicArr.begin();i!=musicArr.end();i++) 56 | { 57 | playlist->addMedia(i->get_url()); 58 | } 59 | } 60 | 61 | void MusicList::add_to_listwidget(MusicListWidget* listWidget) 62 | { 63 | foreach(const MyMusic &i, musicArr) 64 | { 65 | QListWidgetItem *item = new QListWidgetItem; 66 | item->setIcon(listWidget->get_icon()); 67 | item->setText(i.get_brief_info()); 68 | item->setForeground(Qt::black); 69 | item->setSizeHint(QSize(1000, 70)); 70 | item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); 71 | listWidget->addItem(item); 72 | } 73 | } 74 | 75 | void MusicList::sort_by(COMPARE key) 76 | { 77 | sort(musicArr.begin(), musicArr.end(), MusicCompare(key)); 78 | remove_sql_all(); 79 | insert_sql_all(); 80 | } 81 | 82 | void MusicList::neaten() 83 | { 84 | sort(musicArr.begin(), musicArr.end(), MusicCompare(DEFAULT)); 85 | musicArr.erase(unique(musicArr.begin(), musicArr.end(), MusicCompare(EQUALITY)), musicArr.end()); 86 | remove_sql_all(); 87 | insert_sql_all(); 88 | } 89 | 90 | void MusicList::clear() 91 | { 92 | musicArr.clear(); 93 | remove_sql_all(); 94 | } 95 | 96 | void MusicList::remove_sql_all() 97 | { 98 | QSqlQuery sqlQuery; 99 | QString deleteSql = "delete from MusicInfo"; 100 | sqlQuery.prepare(deleteSql); 101 | sqlQuery.exec(); 102 | } 103 | 104 | void MusicList::insert_sql_all() 105 | { 106 | for(auto i = musicArr.begin(); i < musicArr.end(); ++i) 107 | i->insert_sql(); 108 | } 109 | 110 | void MusicList::read_from_sql() 111 | { 112 | QSqlQuery sqlQuery; 113 | QString selectSql = "select url, author, title, duration, albumTitle, bitRate from MusicInfo"; 114 | sqlQuery.prepare(selectSql); 115 | if(sqlQuery.exec()) 116 | { 117 | while(sqlQuery.next()) 118 | { 119 | MyMusic tempMusic; 120 | tempMusic.url = QUrl(sqlQuery.value(0).toString()); 121 | tempMusic.author = sqlQuery.value(1).toString(); 122 | tempMusic.title = sqlQuery.value(2).toString(); 123 | tempMusic.duration = sqlQuery.value(3).toLongLong(); 124 | tempMusic.albumTitle = sqlQuery.value(4).toString(); 125 | tempMusic.bitRate = sqlQuery.value(5).toInt(); 126 | musicArr.push_back(tempMusic); 127 | qDebug() << tempMusic.author << "-" << tempMusic.title << "-" << tempMusic.duration; // 测试用 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MusicList.h: -------------------------------------------------------------------------------- 1 | #ifndef MUSICLIST_H 2 | #define MUSICLIST_H 3 | #include "MyMusic.h" 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class MusicListWidget; 10 | 11 | class MusicList 12 | { 13 | private: 14 | // 所存储的歌曲 15 | vector musicArr; 16 | public: 17 | MusicList() {} 18 | MusicList(const QList& urls); 19 | 20 | // 从url添加歌曲 21 | void add_music(const QList& urls); 22 | 23 | // 添加一首歌曲 24 | void add_music(const MyMusic& iMusic); 25 | 26 | //获取指定位置的歌曲 27 | MyMusic get_music(int pos); 28 | 29 | // 将本歌单加入播放列表 30 | void add_to_playlist(QMediaPlaylist* playlist); 31 | 32 | // 歌单可视化 33 | void add_to_listwidget(MusicListWidget* listWidget); 34 | 35 | // 将本列表中的歌曲排序 36 | void sort_by(COMPARE key); 37 | 38 | // 整理歌单,去掉重复的 39 | void neaten(); 40 | 41 | // 清空本歌单 42 | void clear(); 43 | 44 | // 数据库移除全部歌曲 45 | void remove_sql_all(); 46 | 47 | // 数据库加入全部歌曲 48 | void insert_sql_all(); 49 | 50 | // 从数据库中恢复歌单 51 | void read_from_sql(); 52 | 53 | friend class MainWidget; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MusicListWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MusicListWidget.h" 2 | 3 | MusicListWidget::MusicListWidget(QWidget* parent) : QListWidget(parent) 4 | { 5 | 6 | } 7 | 8 | void MusicListWidget::refresh() 9 | { 10 | clear(); 11 | musicList.add_to_listwidget(this); 12 | } 13 | 14 | void MusicListWidget::set_musiclist(const MusicList& music) 15 | { 16 | musicList = music; 17 | refresh(); 18 | } 19 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MusicListWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MUSICLISTWIDGET_H 2 | #define MUSICLISTWIDGET_H 3 | #include 4 | #include "MusicList.h" 5 | 6 | class MusicListWidget : public QListWidget 7 | { 8 | private: 9 | //当前歌曲列表(存储的是歌曲信息) 10 | MusicList musicList; 11 | //当前展示列表项使用的图标 12 | QIcon icon=QIcon(":/Assets/Icon/music.png"); 13 | public: 14 | MusicListWidget(QWidget* parent = Q_NULLPTR); 15 | 16 | //刷新显示(当musicList有所变化时,需要调用) 17 | void refresh(); 18 | //设置歌曲列表 19 | void set_musiclist(const MusicList& music); 20 | 21 | //设置/获取图标Icon 22 | void set_icon(QIcon iicon){ icon=iicon; } 23 | QIcon get_icon(){ return icon; } 24 | 25 | friend class MainWidget; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MusicPlayerWW.pro: -------------------------------------------------------------------------------- 1 | QT += core gui sql 2 | QT += multimedia 3 | 4 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 5 | 6 | CONFIG += c++11 7 | 8 | # The following define makes your compiler emit warnings if you use 9 | # any Qt feature that has been marked deprecated (the exact warnings 10 | # depend on your compiler). Please consult the documentation of the 11 | # deprecated API in order to know how to port your code away from it. 12 | DEFINES += QT_DEPRECATED_WARNINGS 13 | 14 | # You can also make your code fail to compile if it uses deprecated APIs. 15 | # In order to do so, uncomment the following line. 16 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 17 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 18 | 19 | SOURCES += \ 20 | LyricWidget.cpp \ 21 | MusicList.cpp \ 22 | MusicListWidget.cpp \ 23 | MyMusic.cpp \ 24 | main.cpp \ 25 | MainWidget.cpp 26 | 27 | HEADERS += \ 28 | LyricWidget.h \ 29 | MainWidget.h \ 30 | MusicList.h \ 31 | MusicListWidget.h \ 32 | MyMusic.h \ 33 | MyQSS.h 34 | 35 | FORMS += \ 36 | LyricWidget.ui \ 37 | MainWidget.ui 38 | 39 | # Default rules for deployment. 40 | qnx: target.path = /tmp/$${TARGET}/bin 41 | else: unix:!android: target.path = /opt/$${TARGET}/bin 42 | !isEmpty(target.path): INSTALLS += target 43 | 44 | RESOURCES += \ 45 | Resources.qrc 46 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MyMusic.cpp: -------------------------------------------------------------------------------- 1 | #include "MyMusic.h" 2 | #include 3 | #include // 提供事件循环 4 | #include 5 | #include 6 | 7 | MyMusic::MyMusic(QUrl iurl) 8 | { 9 | url = iurl; 10 | // qDebug() << url.toString(); 11 | init_from_url(); 12 | } 13 | 14 | MyMusic::MyMusic(QString musicName) 15 | { 16 | init_from_name(musicName); 17 | } 18 | 19 | QString my_format_time(qint64 msTime) 20 | { 21 | qint64 seconds = msTime / 1000; // 毫秒变为秒 22 | const qint64 minutes = seconds / 60; 23 | seconds -= minutes * 60; 24 | return QStringLiteral("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')); 25 | } 26 | 27 | void MyMusic::init_from_url() 28 | { 29 | QMediaPlayer tmpPlayer; 30 | tmpPlayer.setMedia(url); 31 | // 元数据的解析需要时间,所以这里需要循环等待 (但同时需要保持Qt事件处理机制在运行) 32 | while(!tmpPlayer.isMetaDataAvailable()) QCoreApplication::processEvents(); 33 | QString list = tmpPlayer.availableMetaData().join(", "); 34 | if(tmpPlayer.isMetaDataAvailable()) 35 | { 36 | author = tmpPlayer.metaData(QStringLiteral("Author")).toStringList().join(","); // 作者这里返回的是StringList 37 | title = tmpPlayer.metaData(QStringLiteral("Title")).toString(); 38 | albumTitle = tmpPlayer.metaData(QStringLiteral("AlbumTitle")).toString(); 39 | duration = tmpPlayer.duration(); 40 | bitRate = tmpPlayer.metaData(QStringLiteral("AudioBitRate")).toInt(); 41 | // 封面图片(应获取"ThumbnailImage" From: https://www.zhihu.com/question/36859497) 42 | picImg = tmpPlayer.metaData(QStringLiteral("ThumbnailImage")).value(); 43 | if(picImg.isNull()) picImg = QImage(":/Assets/Icon/non-music.png"); 44 | } 45 | } 46 | 47 | void MyMusic::init_from_name(QString musicName) 48 | { 49 | QString openDir = QCoreApplication::applicationDirPath() + "/Music"; 50 | url = openDir + "/" + musicName; 51 | init_from_url(); 52 | } 53 | 54 | QString MyMusic::get_brief_info() const 55 | { 56 | return author + " - " + title + " [" + my_format_time(duration) + "]"; 57 | } 58 | 59 | QString MyMusic::get_lyric() 60 | { 61 | QString filePath = url.toLocalFile(); 62 | filePath.replace(".mp3", ".lrc"); 63 | filePath.replace(".flac", ".lrc"); 64 | filePath.replace(".mpga", ".lrc"); 65 | 66 | return filePath; 67 | } 68 | 69 | void MyMusic::insert_sql() 70 | { 71 | QSqlQuery sqlQuery; 72 | QString insertSql = "insert into MusicInfo values (?, ?, ?, ?, ?, ?)"; 73 | sqlQuery.prepare(insertSql); 74 | sqlQuery.addBindValue(url.toString()); 75 | sqlQuery.addBindValue(author); 76 | sqlQuery.addBindValue(title); 77 | sqlQuery.addBindValue(duration); 78 | sqlQuery.addBindValue(albumTitle); 79 | sqlQuery.addBindValue(bitRate); 80 | if(sqlQuery.exec()) qDebug() << "插入数据库"; // 测试用 81 | } 82 | 83 | bool MusicCompare::operator()(const QString& strA, const QString& strB) 84 | { 85 | MyMusic A(strA); 86 | MyMusic B(strB); 87 | switch (key) { 88 | case TITLE: 89 | return A.title < B.title; 90 | case AUTHOR: 91 | return A.author < B.author; 92 | case DURATION: 93 | return A.duration < B.duration; 94 | case EQUALITY: // 对比两者url是否相等 95 | return A.get_url() == B.get_url(); 96 | default: 97 | return A.get_brief_info() < B.get_brief_info(); 98 | } 99 | } 100 | 101 | bool MusicCompare::operator()(const MyMusic &A, const MyMusic &B) 102 | { 103 | switch (key) { 104 | case TITLE: 105 | return A.title < B.title; 106 | case AUTHOR: 107 | return A.author < B.author; 108 | case DURATION: 109 | return A.duration < B.duration; 110 | case EQUALITY: // 对比两者url是否相等 111 | return A.get_url() == B.get_url(); 112 | default: 113 | return A.get_brief_info() < B.get_brief_info(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MyMusic.h: -------------------------------------------------------------------------------- 1 | #ifndef MYMUSIC_H 2 | #define MYMUSIC_H 3 | #include 4 | #include 5 | #include 6 | 7 | // 歌曲类;用于描述单个歌曲 8 | class MyMusic 9 | { 10 | private: 11 | QUrl url; // 歌曲地址 12 | QString author; // 作者 13 | QString title; // 歌名 14 | QString albumTitle; // 唱片集 15 | qint64 duration; // 时长 16 | int bitRate; // 比特率 17 | QImage picImg; // 专辑封面 18 | 19 | void init_from_url(); // 私有方法:根据Url获取歌曲各信息 20 | void init_from_name(QString musicName); // 私有方法:根据歌曲名获取歌曲各信息 21 | 22 | public: 23 | MyMusic() {} 24 | MyMusic(QUrl iurl); 25 | MyMusic(QString musicName); 26 | 27 | QUrl get_url() const {return url;} // 返回歌曲地址 28 | QString get_brief_info() const; // 以一行返回歌曲的信息 29 | QString get_lyric(); // 根据文件名来获取歌词路径 30 | void insert_sql(); // 添加至数据库 31 | 32 | friend class MusicCompare; 33 | friend class MusicList; 34 | friend class MainWidget; 35 | }; 36 | 37 | // 排序的可选属性 38 | enum COMPARE{DEFAULT, TITLE, AUTHOR, DURATION, EQUALITY}; 39 | 40 | // 排序对比类:用于 41 | class MusicCompare 42 | { 43 | COMPARE key; 44 | public: 45 | MusicCompare(){key=DEFAULT;} 46 | MusicCompare(COMPARE ikey){key=ikey;} 47 | bool operator()(const MyMusic& A, const MyMusic& B); 48 | bool operator()(const QString& strA, const QString& strB); 49 | }; 50 | 51 | #endif // MYMUSIC_H 52 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/MyQSS.h: -------------------------------------------------------------------------------- 1 | #ifndef MYQSS_H 2 | #define MYQSS_H 3 | #include 4 | 5 | // 此文件统一存放一些QSS样式代码 6 | 7 | // 播放中的按钮样式 8 | inline QString PlayingStyle() 9 | { 10 | return 11 | "#btnPlay" 12 | "{" 13 | "image:url(:/Assets/Icon/playing.png);" 14 | "background-repeat:no-repeat;" 15 | "background-color:transparent;" 16 | "background-position:center center;" 17 | "border:transparent;" 18 | "}"; 19 | } 20 | // 未在播放的按钮状态 21 | inline QString PauseStyle() 22 | { 23 | return 24 | "#btnPlay" 25 | "{" 26 | "image:url(:/Assets/Icon/pause.png);" 27 | "background-repeat:no-repeat;" 28 | "background-color:transparent;" 29 | "background-position:center center;" 30 | "border:transparent;" 31 | "}"; 32 | } 33 | 34 | // 音量按钮 35 | inline QString VolStyle() 36 | { 37 | return 38 | "#btnVol" 39 | "{" 40 | "image:url(:/Assets/Icon/vol.png);" 41 | "background-repeat:no-repeat;" 42 | "background-color:transparent;" 43 | "background-position:center center;" 44 | "border:transparent;" 45 | "}"; 46 | } 47 | // 音量按钮静音状态 48 | inline QString MuteStyle() 49 | { 50 | return 51 | "#btnVol" 52 | "{" 53 | "image:url(:/Assets/Icon/mute.png);" 54 | "background-repeat:no-repeat;" 55 | "background-color:transparent;" 56 | "background-position:center center;" 57 | "border:transparent;" 58 | "}"; 59 | } 60 | 61 | // 播放模式的切换 62 | // 随机 63 | inline QString RandomStyle() 64 | { 65 | return 66 | "#btnPlayMode" 67 | "{" 68 | "image:url(:/Assets/Icon/random.png);" 69 | "background-repeat:no-repeat;" 70 | "background-color:transparent;" 71 | "background-position:center center;" 72 | "border:transparent;" 73 | "}"; 74 | } 75 | // 列表循环 76 | inline QString LoopStyle() 77 | { 78 | return 79 | "#btnPlayMode" 80 | "{" 81 | "image:url(:/Assets/Icon/loop.png);" 82 | "background-repeat:no-repeat;" 83 | "background-color:transparent;" 84 | "background-position:center center;" 85 | "border:transparent;" 86 | "}"; 87 | } 88 | // 单曲循环 89 | inline QString Loop41Style() 90 | { 91 | return 92 | "#btnPlayMode" 93 | "{" 94 | "image:url(:/Assets/Icon/loop41.png);" 95 | "background-repeat:no-repeat;" 96 | "background-color:transparent;" 97 | "background-position:center center;" 98 | "border:transparent;" 99 | "}"; 100 | } 101 | 102 | inline QString ScrollBarStyle() 103 | { 104 | return 105 | "QScrollBar:vertical" 106 | "{" 107 | "width:80px;" 108 | "background:rgba(248, 248, 255, 0);" 109 | "margin:0px,0px,0px,0px;" 110 | "padding-top:80px;" 111 | "padding-bottom:80px;" 112 | "}" 113 | // 滑块区域 114 | "QScrollBar::handle:vertical" 115 | "{" 116 | "background:rgba(255, 20, 147, 1);" 117 | "min-height:150px;" 118 | // "border-radius: 40px;" 119 | "}" 120 | // 上箭头 121 | "QScrollBar::sub-line:vertical" 122 | "{" 123 | "width:80px;" 124 | "height:80px;" 125 | "border-image:url(:/Assets/Icon/up-arrow.png);" 126 | "subcontrol-position:top;" 127 | "}" 128 | // 下箭头 129 | "QScrollBar::add-line:vertical" 130 | "{" 131 | "width:80px;" 132 | "height:80px;" 133 | "border-image:url(:/Assets/Icon/down-arrow.png);" 134 | "subcontrol-position:bottom;" 135 | "}" 136 | // 滑块以外的区域 137 | "QScrollBar::sub-page:vertical" 138 | "{" 139 | "background:rgb(220, 220, 220);" 140 | "}" 141 | "QScrollBar::add-page:vertical" 142 | "{" 143 | "background:rgb(220, 220, 220);" 144 | "}" 145 | ; 146 | } 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assets/Image/Background1.jpg 4 | Assets/Icon/next.png 5 | Assets/Icon/pause.png 6 | Assets/Icon/pre.png 7 | Assets/Icon/sliderHandle.png 8 | Assets/Icon/vol.png 9 | Assets/Icon/quit.png 10 | Assets/Icon/music.png 11 | Assets/Icon/music-playing.png 12 | Assets/Icon/playing.png 13 | Assets/Icon/non-music.png 14 | Assets/Icon/list.png 15 | Assets/Icon/title.png 16 | Assets/Icon/singer.png 17 | Assets/Icon/duration.png 18 | Assets/Icon/mute.png 19 | Assets/Icon/random.png 20 | Assets/Icon/loop.png 21 | Assets/Icon/loop41.png 22 | Assets/Icon/folder.png 23 | Assets/Icon/neaten.png 24 | Assets/Icon/up-arrow.png 25 | Assets/Icon/down-arrow.png 26 | Assets/Icon/sweep.png 27 | 28 | 29 | -------------------------------------------------------------------------------- /x86/MusicPlayerWW/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWidget w; 9 | if(argc < 2) w.show(); 10 | if(argc >= 2 && argv[1] == QStringLiteral("-hide")) w.hide(); 11 | return a.exec(); 12 | } 13 | --------------------------------------------------------------------------------