Аудио плейлист в QML
Я делаю простой плейлист на qml. Я имею в виду, что есть Audio
плеер, который воспроизводит файлы с расширением .mp3
в папке. Но этот "плейлист" предполагает, что вся папка является плейлистом. Поэтому я даю путь к папке списка воспроизведения в качестве аргумента командной строки для программы, например. ./playlist_player /home/user/playlist-folder
и программа воспроизводит целые mp3-файлы в папке playlist-folder. Но так как qml не понимает подстановочные знаки, такие как звездочка (*), я использую QDir
чтобы найти имена mp3-файлов, чтобы найти имена mp3-файлов и представить эти строки в qml, используя подход, который описан здесь http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html. Итак, у меня есть имена классов, производные от QObject FileNames
и это имеет Q_PROPERTY(QStringList mp3List READ mp3List)
, Так что на конструкторе FileNames
Я ищу путь, указанный в командной строке, и обнаруживаю файлы с расширением.mp3s и push_back эти пути к FileNames::mp3List
, И на main.cpp
Я создаю экземпляр FileNames
объект после этого экземпляра QQuickView
объект, и я передаю FileNames
объект на стороне Qml с QQmlContext::setContextProperty
функция-член.
Все работает до тех пор, пока мне не понадобится также количество mp3-файлов в списке для повторения всего next
функция, которая увеличивает index
значение для списка на стороне qml. Я знаю, что могу выставить другое свойство для прохождения подсчета 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
}
}
Так есть ли у кого-нибудь более нативное решение для создания приложения "playlist_player" с Qt?
ОБНОВЛЕНИЕ ->
Так что сейчас я использую FolderListModel
но кажется, что этот класс не работает должным образом без представления. Я думаю, потому что это работает асинхронно. Вот как выглядит мой код;
/* 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
}
}
Спасибо, Сина.
2 ответа
Использование типов C++ в QML или определение свойств контекста прекрасно подходит, когда требуется интеграция с кодом C++. C++ естественным образом расширяет возможности QML, и это только вопрос понимания, когда нужен внутренний класс C++ или новый тип QML, написанный на C++. Для дальнейшего обсуждения этой темы, пожалуйста, посмотрите на этот другой ответ.
Учитывая ответ, связанный выше, и время жизни вашего компонента списка воспроизведения, использование QML-типа вместо свойства context будет правильным выбором. Тем не менее, как уже было сказано, QML уже предоставляет вам нужный компонент: FolderListModel
, В вашем случае это может быть легко определено так:
FolderListModel {
id: folderModel
nameFilters: ["*.mp3"]
showDirs: false
folder: "file:" + /* CPP PROVIDED PATH */
}
и вы можете использовать get
функция для запроса следующего mp3 в списке, в соответствии с count
имущество.
Большое преимущество в том, что FolderListModel
действительно прослушивает изменения файлов в вашей папке, так что вам не нужно проверять добавление / удаление файлов. Последний, но тем не менее важный, FolderListModel
может быть легко интегрирован в реализацию GUI, используя его в качестве модели для ListView
, Пожалуйста, смотрите здесь для примера.
добавление
Как отметил ОП FolderListModel
работает асинхронно Это обычное поведение, поскольку выполнение всех проверок во время инициализации компонента может привести к длительному запуску (особенно для каталогов, заполненных файлами). Там нет собственности, чтобы отслеживать, когда FolderListModel
завершил отслеживание файловой системы, и, вероятно, не имеет смысла иметь такую, поскольку проверка файловой системы является непрерывной. Добавление bool
свойство может помочь, что-то вроде этого:
FolderListModel {
id: fm
folder: "..."
showDirs: false
showDotAndDotDot: false
nameFilters: ["*.mp3"]
property bool ready: count > 0
}
когда ready
Значение true (поскольку mp3-файлы были добавлены или начальное сканирование завершено), возможно, можно запустить проигрыватель. Очевидно, что другие свойства могут быть добавлены для улучшения поведения модели.
FolderListModel
QML Type будет делать то, что вам нужно. Поддерживает фильтрацию имен и т. Д.