Аудио плейлист в 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 будет делать то, что вам нужно. Поддерживает фильтрацию имен и т. Д.

Другие вопросы по тегам