windows Qt 5.15
视频相关:播放暂停、播放进度调节、音量调节、列表显示指定目录下的视频文件
音频相关:播放暂停、上一曲下一曲、随机播放和顺序播放、播放进度调节、歌词滚动以及高亮、列表显示指定目录下的音频文件
其他功能:支持皮肤切换、窗口最大化最小化、窗口任意形状调节、窗口移动、边框阴影
视频列表通过TableView实现,其代理(delegate)要显示的数据来自videoplayer.h的m_videoTableModel
,当双击列表某一行时,触发VideoTable.qml中自定义的信号doubleClicked(int row)
,该信号将页面切换到VideoSecondPage.qml(播放视频的页面),同时把双击的行号传给VideoSecondPage.qml,这样VideoSecondPage.qml就可以根据行号调用C++函数获取当前双击行对应的视频路径,因此就可以实现视频的播放
// 切换页面并传递行号 VideoPanel.qml
VideoFirstPage {
onTableRowDoubleClicked: { // 其实就是VideoTable.qml的doubleClicked信号
root.selectedRow = row // 将选中的行号赋值给root.selectedRow便于传入到secondPage中
stackView.push(secondPage) // 跳转到第二个页面(播放视频的页面)
}
}
// 视频播放器 VideoSecondPage.qml
VideoPlayer {
id: videoPlayer
anchors.left: parent.left
anchors.right: parent.right
height: width * 9 / 16
source: myVideoPlayer.getVideoPathByRow(root.selectedRow)
}
播放音频实现:音频列表通过TableView实现,其代理(delegate)要显示的数据来自musicplayer.h的m_musicTableModel
,当双击列表某一行时,触发MusicTable.qml中自定义的信号doubleClicked(int row)
,该信号把双击的行号传给MusicControlBar.qml(控制音频播放等页面),这样MusicControlBar.qml就可以根据行号调用C++函数获取当前双击行对应的视频路径,因此就可以实现视频的播放
上一曲/下一曲实现:MusicControlBar.qml有上一曲下一曲按钮,实现切歌只需要改变MusicControlBar.qml的seclectedRow
属性即可,seclectedRow
记录当前播放的音乐在列表的哪一行。当点击下一曲后,将seclectedRow
增加1即可,不需要其他操作,因为MediaPlayer
(位于MusicControlBar.qml中)的source
属性是和seclectedRow
是相互绑定的,只要seclectedRow
改变,MediaPlayer
的source
也会相应改变,而source
的改变又会触发onSourceChanged
信号,onSourceChanged
实现了音频的播放,主要代码如下:
// 下一曲按钮
CusWidgets.ImageButton {
...省略代码一万行
onClicked: {
if (root.playMode === root.order) {// 如果是顺序播放
if (root.seclectedRow !== -1) {
if (root.seclectedRow === myMusicPlayer.musicTableModel.rowCount()-1) {
root.seclectedRow = 0
} else {
root.seclectedRow += 1
}
} else {
root.seclectedRow = 0
}
} else { // 如果是随机播放
root.seclectedRow = getRandomNum(0, myMusicPlayer.musicTableModel.rowCount())
}
}
}
MediaPlayer {
id: musicPlayer
source: root.seclectedRow === -1 ? "" : myMusicPlayer.getMusicPathByRow(root.seclectedRow)
onSourceChanged: {
musicPlayer.play()
}
}
歌词显示实现:
(1)歌词模型的更新:前面说到,切歌只需要改变MusicControlBar.qml自定义的seclectedRow
属性即可,当seclectedRow
改变后,其触发的信号就可以实现对歌词模型的更新
MusicControlBar { // 位于MusicPanel.qml
id: musicControlBar
onSeclectedRowChanged: {
myMusicPlayer.upDateLyricModelBy(seclectedRow) //调用C++函数来更新歌词信息
}
}
(2)歌词滚动实现: LRC歌词文件如下:
// 第一个数字表示分钟,第二个数字表示秒,第三个数字可能是毫秒
// 不过我们只用到前两个数字和歌词内容
[0:0.250.00]沉默是金 - 张国荣
[0:1.500.00]词:许冠杰
[0:2.190.00]曲:张国荣
[0:27.450.00]夜风凛凛 独回望旧事前尘
[0:33.120.00]是以往的我充满怒愤
// 另一种lrc歌词文件格式是下面这样的
// 第一个数字是分钟,第二个数字是秒,第三个数字是多少个10毫秒,也就是说,
// 第三个数最大为100
[ver:v1.0] // 我的程序为了方便没有解析这些信息
[ti:家]
[00:00.36]家 - 许巍
[00:01.04]词:许巍
[00:01.69]曲:许巍
[00:37.95]拥抱着亲人的时候
了解歌词格式后,可以通过正则表达式解析歌词(见lyrics.h的parseLyrics
函数),该函数把歌词的时间戳(比如[0:27.450.00]
)和歌词对应的索引(索引标记着这是第几句歌词,比如[0:27.450.00]
对应的索引为3,索引从0开始)存储到QMap,即下面代码的m_lyric2IdxMap
中,时间戳是键,索引是值,然后把歌词内容(比如:[0:27.450.00]
对应歌词:夜风凛凛 独回望旧事前尘)存储到歌词模型LyricModel
中,LyricModel
其实就是对QStringList
进行管理。歌词解析完成后,接下来就是实现歌词滚动,如下面代码所示,我们可以在MediaPlayer
的onPositionChanged
信号处理中,获取当前应该显示歌词的索引号,获取索引的思路是根据当前音乐播放进度(即形参qint64 pos
的值),遍历上述m_lyric2IdxMap
,找到pos
在那个歌词的时间段内,然后返回该歌词在LyricModel
的索引。
MediaPlayer {
onPositionChanged: {
// 实时显示歌词 调用c++函数,返回此时应该显示的歌词的索引
var idx = myMusicPlayer.getLyricIdxByPosition(position)
if (idx === -1) {
root.lyricIdx = -1 // -1表示当前position没有合适的歌词
} else {
root.lyricIdx = idx
}
}
}
// c++函数,见musicplayer.cpp
int MusicPlayer::getLyricIdxByPosition(qint64 pos)
{
QMap<qint64, int>::iterator iter = m_lyric2IdxMap.begin();
while (iter != m_lyric2IdxMap.end())
{
if ((iter.key()-500<=pos) && (iter+1).key()-500 > pos)
{
return iter.value();
}
iter++;
}
return -1;
}
根据播放进度获得当前歌词索引后,只需要吧该歌词索引值赋值给显示歌词的ListView
的currentIndex
属性即可
ListView {
id: lyricsList
currentIndex: lyricIndex===-1?currentIndex:lyricIndex
// 省略代码一万行
}
点击左侧导航栏的音频播放,然后最大化窗口,点击左侧视频播放,发现视频表格并没有随着窗口宽度变化而发生变化,不知道为什么,明明已经再TableView的onWidthChangede
中调用了forceLayout()
。如果解决了请告知一声
TableView {
id: tableView
// 省略代码
columnWidthProvider: function(colum) { return root.columnWidths[colum] }
onWidthChanged: {
// If you change the values that a rowHeightProvider or a columnWidthProvider
// return for rows and columns inside the viewport, you must call forceLayout.
// This informs TableView that it needs to use the provider functions again to
// recalculate and update the layout.
forceLayout()
}
}
皮肤切换实现:玩转Qml(3)-换皮肤 | 涛哥的博客
解析歌词:基于Qt的网络音乐播放器(五)实现歌词滚动显示_qt歌词滚动_花狗Fdog的博客-CSDN博客;QML中ListView向上滚动效果_hp_cpp的博客-CSDN博客