QML 中的音频播放列表
Audio playlist in QML
我正在 qml 上制作一个简单的播放列表播放器。我的意思是有一个 Audio
播放器可以播放文件夹中扩展名为 .mp3
的文件。但是这个 "playlist player" 将整个文件夹假定为一个播放列表。所以我将播放列表文件夹的路径作为命令行参数提供给程序,例如。 ./playlist_player /home/user/playlist-folder
程序播放播放列表文件夹中的整个 mp3。但是由于 qml 不理解像星号 (*) 这样的通配符,我使用 QDir
来查找 mp3 的名称来查找 mp3 的名称,并使用此处描述的方法将这些字符串公开给 qml http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html。所以我有一个 QObject 派生的 class 名称 FileNames
并且它有 Q_PROPERTY(QStringList mp3List READ mp3List)
。因此,在 FileNames
的构造函数中,我查找命令行中给出的路径,并检测扩展名为 .mp3s 和 push_back 的文件 FileNames::mp3List
的路径。在 main.cpp
上,我在实例化 QQuickView
对象之后实例化了一个 FileNames
对象,并将 FileNames
对象传递给带有 QQmlContext::setContextProperty
成员函数的 Qml 端。
一切正常,直到我还需要列表中的 mp3 的数量以使用 next
函数遍历,该函数在 qml 端增加列表的 index
值。我知道我可以公开另一个 属性 来传递 mp3List
的计数,但我最终得到的这个解决方案可能不是最好的本地解决方案。
这是我写的代码;
/* filenames.h */
class FileNames : public QObject {
Q_OBJECT
Q_PROPERTY(QStringList mp3List READ mp3List)
Q_PROPERTY(int mp3ListCount READ mp3ListCount)
public:
explicit FileNames(QObject *parent = 0);
QStringList mp3List() const;
int mp3ListCount() const;
private:
QStringList m_mp3List;
int m_mp3ListCount;
};
/* filenames.cpp */
FileNames::FileNames(QObject *parent) :
QObject(parent), m_mp3ListCount(0)
{
QString path("/home/user/music/");
QDir dirname(path);
QStringList dir = dirname.entryList();
for (const auto &file : dir)
if (file.endsWith(".mp3")) {
m_mp3List.push_back("file://" + path + file);
++m_mp3ListCount;
}
}
QStringList FileNames::mp3List() const
{
return m_mp3List;
}
int FileNames::mp3ListCount() const
{
return m_mp3ListCount;
}
/* main.cpp */
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
FileNames names;
QQuickView view;
view.engine()->rootContext()->setContextProperty("names", &names);
view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.showFullScreen();
return app.exec();
}
/* Playlist.qml */
Item {
id: root
property int index: 0
property MediaPlayer mediaPlayer
property variant list;
property int listCount;
function setIndex(i)
{
console.log("setting index to: " + i);
index = i;
if (index < 0 || index >= listCount) {
index = 0;
mediaPlayer.source = "";
}
mediaPlayer.source = list[index];
}
function next()
{
setIndex(index + 1);
}
function previous()
{
setIndex(index + 1);
}
Connections {
target: root.mediaPlayer
onStopped: {
if (root.mediaPlayer.status == MediaPlayer.EndOfMedia)
{
root.next();
root.mediaPlayer.play();
}
}
}
}
/* main.qml */
Rectangle {
id: root
width: 1024
height: 600
color: "black"
Playlist {
id: playlist
mediaPlayer: player
list: names.mp3List
listCount: names.mp3ListCount
}
MediaPlayer {
id: player
}
VideoOutput {
anchors.fill: parent
source: player
}
}
那么,有没有人有更原生的解决方案来使用 Qt 制作 "playlist_player" 应用程序?
更新 ->
所以现在我正在使用 FolderListModel
但似乎 class 在没有视图的情况下无法正常工作。我猜是因为它是异步工作的。这是我的代码的样子;
/* Playlist.qml */
Item {
id: root
property int index: 0
property MediaPlayer mediaPlayer
property FolderListModel fm
function setIndex(i)
{
index = i;
console.log("setting index to: " + i);
index %= fm.count;
mediaPlayer.source = "file://" + fm.get(index, "filePath");
console.log("setting source to: " + mediaPlayer.source);
}
function next()
{
setIndex(index + 1);
}
function previous()
{
setIndex(index + 1);
}
Connections {
target: root.mediaPlayer
onStopped: {
if (root.mediaPlayer.status == MediaPlayer.EndOfMedia) {
root.next();
root.mediaPlayer.play();
}
}
}
}
/* main.qml */
Rectangle {
id: root
width: 1024
height: 600
color: "black"
property bool onStart: true
Playlist {
id: playlist
mediaPlayer: player
fm: FolderListModel {
id: fm
folder: "file:///home/user/music"
showDirs: false
showDotAndDotDot: false
nameFilters: ["*.mp3"]
property bool ready: count > 0
// startup initialization;
onReadyChanged: if (player.status == MediaPlayer.NoMedia) {
playlist.setIndex(0);
player.play();
}
}
}
MediaPlayer { id: player }
VideoOutput {
anchors.fill: parent
source: player
}
}
谢谢,
新浪.
FolderListModel
QML 类型将满足您的需求。支持名称过滤等
当需要与 C++ 代码集成时,在 QML 中使用 C++ 类型或定义上下文属性非常好。 C++ 自然地扩展了 QML 的可能性,当需要 C++ 后端 class 或用 C++ 编写的新 QML 类型时,这只是一个理解问题。有关此主题的进一步讨论,请查看 。
鉴于上面链接的答案和播放列表组件的生命周期,使用 QML 类型而不是上下文 属性 将是正确的选择。然而,如前所述,QML 已经为您提供了正确的组件:FolderListModel
。在你的情况下,它可以很容易地定义如下:
FolderListModel {
id: folderModel
nameFilters: ["*.mp3"]
showDirs: false
folder: "file:" + /* CPP PROVIDED PATH */
}
您可以使用 get
function to query for the next mp3 in the list, according to the count
属性。
一个很大的优势是 FolderListModel
会 侦听文件夹中的文件更改,这样您 不需要 检查文件 addition/removal。最后但同样重要的是,FolderListModel
可以轻松集成到 GUI 实现中,将其用作 ListView
的模型。请参阅 here 示例。
附录
如 OP 所述,FolderListModel
异步工作。这是一种常见的行为,因为在组件初始化期间进行所有检查可能会导致启动时间过长(尤其是对于充满文件的目录)。当 FolderListModel
完成跟踪文件系统时,没有 属性 可以跟踪,而且由于文件系统检查是连续的,因此拥有一个可能也没有意义。添加 bool
属性 可能会有所帮助,如下所示:
FolderListModel {
id: fm
folder: "..."
showDirs: false
showDotAndDotDot: false
nameFilters: ["*.mp3"]
property bool ready: count > 0
}
当ready
为真时(因为已添加mp3或初始扫描已完成)可能会触发播放器。显然可以添加其他属性来改进模型的行为。
我正在 qml 上制作一个简单的播放列表播放器。我的意思是有一个 Audio
播放器可以播放文件夹中扩展名为 .mp3
的文件。但是这个 "playlist player" 将整个文件夹假定为一个播放列表。所以我将播放列表文件夹的路径作为命令行参数提供给程序,例如。 ./playlist_player /home/user/playlist-folder
程序播放播放列表文件夹中的整个 mp3。但是由于 qml 不理解像星号 (*) 这样的通配符,我使用 QDir
来查找 mp3 的名称来查找 mp3 的名称,并使用此处描述的方法将这些字符串公开给 qml http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html。所以我有一个 QObject 派生的 class 名称 FileNames
并且它有 Q_PROPERTY(QStringList mp3List READ mp3List)
。因此,在 FileNames
的构造函数中,我查找命令行中给出的路径,并检测扩展名为 .mp3s 和 push_back 的文件 FileNames::mp3List
的路径。在 main.cpp
上,我在实例化 QQuickView
对象之后实例化了一个 FileNames
对象,并将 FileNames
对象传递给带有 QQmlContext::setContextProperty
成员函数的 Qml 端。
一切正常,直到我还需要列表中的 mp3 的数量以使用 next
函数遍历,该函数在 qml 端增加列表的 index
值。我知道我可以公开另一个 属性 来传递 mp3List
的计数,但我最终得到的这个解决方案可能不是最好的本地解决方案。
这是我写的代码;
/* filenames.h */
class FileNames : public QObject {
Q_OBJECT
Q_PROPERTY(QStringList mp3List READ mp3List)
Q_PROPERTY(int mp3ListCount READ mp3ListCount)
public:
explicit FileNames(QObject *parent = 0);
QStringList mp3List() const;
int mp3ListCount() const;
private:
QStringList m_mp3List;
int m_mp3ListCount;
};
/* filenames.cpp */
FileNames::FileNames(QObject *parent) :
QObject(parent), m_mp3ListCount(0)
{
QString path("/home/user/music/");
QDir dirname(path);
QStringList dir = dirname.entryList();
for (const auto &file : dir)
if (file.endsWith(".mp3")) {
m_mp3List.push_back("file://" + path + file);
++m_mp3ListCount;
}
}
QStringList FileNames::mp3List() const
{
return m_mp3List;
}
int FileNames::mp3ListCount() const
{
return m_mp3ListCount;
}
/* main.cpp */
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
FileNames names;
QQuickView view;
view.engine()->rootContext()->setContextProperty("names", &names);
view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.showFullScreen();
return app.exec();
}
/* Playlist.qml */
Item {
id: root
property int index: 0
property MediaPlayer mediaPlayer
property variant list;
property int listCount;
function setIndex(i)
{
console.log("setting index to: " + i);
index = i;
if (index < 0 || index >= listCount) {
index = 0;
mediaPlayer.source = "";
}
mediaPlayer.source = list[index];
}
function next()
{
setIndex(index + 1);
}
function previous()
{
setIndex(index + 1);
}
Connections {
target: root.mediaPlayer
onStopped: {
if (root.mediaPlayer.status == MediaPlayer.EndOfMedia)
{
root.next();
root.mediaPlayer.play();
}
}
}
}
/* main.qml */
Rectangle {
id: root
width: 1024
height: 600
color: "black"
Playlist {
id: playlist
mediaPlayer: player
list: names.mp3List
listCount: names.mp3ListCount
}
MediaPlayer {
id: player
}
VideoOutput {
anchors.fill: parent
source: player
}
}
那么,有没有人有更原生的解决方案来使用 Qt 制作 "playlist_player" 应用程序?
更新 ->
所以现在我正在使用 FolderListModel
但似乎 class 在没有视图的情况下无法正常工作。我猜是因为它是异步工作的。这是我的代码的样子;
/* Playlist.qml */
Item {
id: root
property int index: 0
property MediaPlayer mediaPlayer
property FolderListModel fm
function setIndex(i)
{
index = i;
console.log("setting index to: " + i);
index %= fm.count;
mediaPlayer.source = "file://" + fm.get(index, "filePath");
console.log("setting source to: " + mediaPlayer.source);
}
function next()
{
setIndex(index + 1);
}
function previous()
{
setIndex(index + 1);
}
Connections {
target: root.mediaPlayer
onStopped: {
if (root.mediaPlayer.status == MediaPlayer.EndOfMedia) {
root.next();
root.mediaPlayer.play();
}
}
}
}
/* main.qml */
Rectangle {
id: root
width: 1024
height: 600
color: "black"
property bool onStart: true
Playlist {
id: playlist
mediaPlayer: player
fm: FolderListModel {
id: fm
folder: "file:///home/user/music"
showDirs: false
showDotAndDotDot: false
nameFilters: ["*.mp3"]
property bool ready: count > 0
// startup initialization;
onReadyChanged: if (player.status == MediaPlayer.NoMedia) {
playlist.setIndex(0);
player.play();
}
}
}
MediaPlayer { id: player }
VideoOutput {
anchors.fill: parent
source: player
}
}
谢谢, 新浪.
FolderListModel
QML 类型将满足您的需求。支持名称过滤等
当需要与 C++ 代码集成时,在 QML 中使用 C++ 类型或定义上下文属性非常好。 C++ 自然地扩展了 QML 的可能性,当需要 C++ 后端 class 或用 C++ 编写的新 QML 类型时,这只是一个理解问题。有关此主题的进一步讨论,请查看
鉴于上面链接的答案和播放列表组件的生命周期,使用 QML 类型而不是上下文 属性 将是正确的选择。然而,如前所述,QML 已经为您提供了正确的组件:FolderListModel
。在你的情况下,它可以很容易地定义如下:
FolderListModel {
id: folderModel
nameFilters: ["*.mp3"]
showDirs: false
folder: "file:" + /* CPP PROVIDED PATH */
}
您可以使用 get
function to query for the next mp3 in the list, according to the count
属性。
一个很大的优势是 FolderListModel
会 侦听文件夹中的文件更改,这样您 不需要 检查文件 addition/removal。最后但同样重要的是,FolderListModel
可以轻松集成到 GUI 实现中,将其用作 ListView
的模型。请参阅 here 示例。
附录
如 OP 所述,FolderListModel
异步工作。这是一种常见的行为,因为在组件初始化期间进行所有检查可能会导致启动时间过长(尤其是对于充满文件的目录)。当 FolderListModel
完成跟踪文件系统时,没有 属性 可以跟踪,而且由于文件系统检查是连续的,因此拥有一个可能也没有意义。添加 bool
属性 可能会有所帮助,如下所示:
FolderListModel {
id: fm
folder: "..."
showDirs: false
showDotAndDotDot: false
nameFilters: ["*.mp3"]
property bool ready: count > 0
}
当ready
为真时(因为已添加mp3或初始扫描已完成)可能会触发播放器。显然可以添加其他属性来改进模型的行为。