Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*)
-
Goodmorning to all,
Qt version 5.12, in a cross compiled environment for raspberrypi using b2qt.
I'm trying to make a music Player where I search for songs in system folders, save them in playlists and make them available as model for view. Then in QML I would select the playlist and load it in MediaPlayer.
Here the code:PlaylistModel.h
#ifndef PLAYLISTMODEL_H #define PLAYLISTMODEL_H #include <QAbstractListModel> #include <QHash> #include <QDir> #include <vector> #include <memory> #include <QtMultimedia/QMediaPlaylist> #include "myplaylist.h" #include "playListModel_global.h" class PLAYLISTMODEL_EXPORT PlayListModel : public QAbstractListModel { Q_OBJECT public: enum PlaylistRoles { IndexRole = Qt::UserRole + 1, IconRole, PlaybackRole, NextRole, PreviousRole, SizeRole, NameRole }; PlayListModel(QDir dirPath, QObject* parent = nullptr); PlayListModel(QString dirPath, QObject* parent = nullptr); QStringList playListNames(); Q_INVOKABLE QMediaPlaylist& getPlaylistFromIndex(int index); // QAbstractItemModel interface int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index = QModelIndex(), int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole) override; QHash<int, QByteArray> roleNames() const override; private: bool isIndexValid(const QModelIndex& index) const; std::vector<std::unique_ptr<myPlaylist>> mPlayListsList; }; #endif // PLAYLISTMODEL_H
PlaylistModel.c
#include "playlistmodel.h" #include "playlistmodel.h" #include <QDirIterator> #include <QMediaContent> #include <QImage> PlayListModel::PlayListModel(QString dirPath, QObject* parent) : PlayListModel(QDir(dirPath), parent) {} QStringList PlayListModel::playListNames() { QStringList lista; for(int i=0; i<rowCount(); i++) { myPlaylist& newPlaylist = *mPlayListsList.at(static_cast<unsigned int>(i)); lista.append(newPlaylist.getPlaylistName()); } return lista; } PlayListModel::PlayListModel(QDir dirPath, QObject* parent) : QAbstractListModel(parent) { QDirIterator it(dirPath.path(), QDirIterator::Subdirectories); while (it.hasNext()) { if (QFileInfo(it.filePath()).isDir() & (it.fileName()!=".") & (it.fileName()!="..")) { bool songFound=false; QDir dir(it.filePath()); qDebug()<<"Analizying folder: "<<it.filePath(); std::unique_ptr<myPlaylist> newPlaylist(new myPlaylist()); newPlaylist->setPlaylistName(it.fileName()); QDirIterator song(dir.path(), QDir::Files); while(song.hasNext()) { qDebug()<<"Analizying file: "<<song.filePath(); if((QFileInfo(song.filePath()).suffix() == "mp3") | (QFileInfo(song.filePath()).suffix() == "m4a")) { QUrl url = QUrl::fromLocalFile(song.path()); newPlaylist->addMedia(QMediaContent(url)); songFound=true; } song.next(); } if(songFound) { int rowIndex = rowCount(); beginInsertRows(QModelIndex(), rowIndex, rowIndex); mPlayListsList.push_back(move(newPlaylist)); endInsertRows(); } } it.next(); } } QMediaPlaylist& PlayListModel::getPlaylistFromIndex(int index) { return *mPlayListsList.at(static_cast<unsigned int>(index)); } int PlayListModel::rowCount(const QModelIndex &parent) const { (void)parent; return static_cast<int>(mPlayListsList.size()); } QVariant PlayListModel::data(const QModelIndex &index, int role) const { if (!isIndexValid(index)) { return QVariant(); } const myPlaylist& album = *mPlayListsList.at(static_cast<unsigned int>(index.row())); switch (role) { case PlaylistRoles::IconRole: return album.getPlaylistImage(); case PlaylistRoles::IndexRole: return album.currentIndex(); case PlaylistRoles::PlaybackRole: return album.playbackMode(); case PlaylistRoles::SizeRole: return album.mediaCount(); case PlaylistRoles::NameRole: case Qt::DisplayRole: return album.getPlaylistName(); //album.objectName(); case PlaylistRoles::NextRole: case PlaylistRoles::PreviousRole: return QVariant(); default: return QVariant(); } } bool PlayListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!isIndexValid(index) || role != PlaylistRoles::PlaybackRole || role != PlaylistRoles::NextRole || role != PlaylistRoles::PreviousRole || role != PlaylistRoles::NameRole || role != PlaylistRoles::IconRole || role != PlaylistRoles::IndexRole) { return false; } myPlaylist& album = *mPlayListsList.at(static_cast<unsigned int>(index.row())); switch(role) { case PlaylistRoles::NameRole: album.setPlaylistName(value.toString()); break; case PlaylistRoles::IconRole: album.setPlaylistImage(value.value<QImage>()); break; case PlaylistRoles::PlaybackRole: //CurrentItemOnce, CurrentItemInLoop, Sequential, Loop, Random album.setPlaybackMode(static_cast<QMediaPlaylist::PlaybackMode>(value.toInt())); break; case PlaylistRoles::NextRole: album.next(); break; case PlaylistRoles::PreviousRole: album.previous(); break; case PlaylistRoles::IndexRole: album.setCurrentIndex(value.toInt()); break; default: break; } emit dataChanged(index, index); return true; } QHash<int, QByteArray> PlayListModel::roleNames() const { QHash<int, QByteArray> roles; roles[PlaylistRoles::IconRole] = "icon"; roles[PlaylistRoles::PlaybackRole] = "playback"; roles[PlaylistRoles::SizeRole] = "size"; roles[PlaylistRoles::IndexRole] = "currentIndex"; roles[PlaylistRoles::NameRole] = "name"; return roles; } bool PlayListModel::isIndexValid(const QModelIndex &index) const { if (index.row() < 0 || index.row() >= rowCount() || !index.isValid()) { return false; } return true; }
MusicPage.qml
import QtQuick 2.6 import QtQuick.Controls 2.0 import QtMultimedia 5.9 import "." import "../menu" PageTheme { property string playlistName property int playlistIndex toolbarTitle: playlistName + " " + playlistIndex MediaPlayer { id: playMusic autoLoad: true autoPlay: true playlist: {playlistModel.getPlaylistFromIndex(playlistIndex)} } MusicController { songPlayer: {playlistModel.getPlaylistFromIndex(playlistIndex)} } }
For information the myPlaylist class is:
#ifndef MYPLAYLIST_H #define MYPLAYLIST_H #include <QtMultimedia/QMediaPlaylist> #include <QImage> class myPlaylist : public QMediaPlaylist { Q_OBJECT public: explicit myPlaylist(QObject* parent = nullptr); void setPlaylistName(QString name); QString getPlaylistName() const; void setPlaylistImage(QImage image); QImage getPlaylistImage() const; private: QString mName; QImage mImage; }; #endif // MYPLAYLIST_H
When I select the playlist in a pathView, the MusicPage QML page opens and I get the error Unable to assign [undefined] to ::QDeclarativeAudio*) for the line: playlist: {playlistModel.getPlaylistFromIndex(playlistIndex)}
What I'm doing wrong? Any suggestions for the whole structure are welcome.
Thank you in advanced.
Regards,
Davidino -
@davidino said in Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*):
Q_INVOKABLE QMediaPlaylist& getPlaylistFromIndex(int index);
You should return a pointer. And in case of unique_ptr, use .get().
-
Heelo @sierdzio ,
thank you for your reply. I've changed the code as :
QMediaPlaylist* PlayListModel::getPlaylistFromIndex(int index) { return mPlayListsList.at(static_cast<unsigned int>(index)).get(); }
But I get the error Unknown method return type: QMediaPlaylist*
Any advices?For information, I pass the Playlist model object as follow:
PlayListModel playlistModel("/home/root"); QQmlApplicationEngine engine; QQmlContext* context = engine.rootContext(); context->setContextProperty("playlistModel", &playlistModel);
Thank you.
Regards.
Davidino -
@davidino said in Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*):
But I get the error Unknown method return type: QMediaPlaylist*
This should work:
Q_DECLARE_METATYPE(QMediaPlaylist*)
. -
@sierdzio said in Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*):
Q_DECLARE_METATYPE(QMediaPlaylist*)
Hello @sierdzio ,
thank you for your message. I tried to add Q_DECLARE_METATYPE as per you indication in playlistModel.h:Q_DECLARE_METATYPE(QMediaPlaylist*) class PLAYLISTMODEL_EXPORT PlayListModel : public QAbstractListModel { Q_OBJECT public:
But I still get the error for the line playlistModel.getPlaylistFromIndex(playlistIndex)
Thank you for your assistance.Regards,
Davidino -
@davidino said in Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*):
But I still get the error for the line playlistModel.getPlaylistFromIndex(playlistIndex)
The problem is that you property is set once/statically and there is no update, you should do something like this:
PageTheme { property string playlistName property int playlistIndex toolbarTitle: playlistName + " " + playlistIndex onPlaylistIndexChanged { var item = playlistModel.getPlaylistFromIndex(playlistIndex) //note: using double ! to check item is not null or undefined (little JavaScript trick) if(!!item) { playMusic.playlist = item; musicCtrl.songPlayer = item; } } MediaPlayer { id: playMusic autoLoad: true autoPlay: true } MusicController { id: musicCtrl } }
-
Hello @KroMignon ,
thank you for your answer. Using this method for retrieving the playlist:
QMediaPlaylist& PlayListModel::getPlaylistFromIndex(int index)
{
return *mPlayListsList.at(static_cast<unsigned int>(index));
}
I don't get any error as before, but the check (if item is null) always return null.
As you can see below I modified the code so that the code show the number of songs in the selected playlist and there are songs! I don't get what it could be..import "QUItMeterComponent" import "../menu" PageTheme { property string playlistName property int playlistIndex property int playlistSize id: musicPage toolbarTitle: playlistName + ": " + playlistSize + " songs" function changePlaylist() { var item = playlistModel.getPlaylistFromIndex(playlistIndex) //note: using double ! to check item is not null or undefined (little JavaScript trick) if(!!item) { playMusic.playlist = item; console.log("playlist changed!!"); } else console.log("playlist no good!!"); } onPlaylistIndexChanged: changePlaylist() MediaPlayer { id: playMusic autoLoad: true autoPlay: true //Component.onCompleted: changePlaylist() }
For information, if I tried the function getPlaylistFromIndex returning the pointer as written above, I still get the error Unknown method return type: QMediaPlaylist*.
Thank you for your assistance.
Regards,
Davidino -
@davidino said in Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*):
QMediaPlaylist& PlayListModel::getPlaylistFromIndex(int index)
As @sierdzio already written, to interface with QML you better return a pointer and not a reference. Please respect QML/C++ best practice, this will avoid you many problems!
have you taken time to read documentation? ==> https://doc.qt.io/qt-5/qtqml-cppintegration-overview.html
-
Hello @KroMignon ,
thank you for your posts I read several links inside it, however I have to say that since there are many possibilities, it gets complicated as well. I search on the net as well. Here some considerations:
First, I tried, instead of returning QMediaPlayer*, to return QObject* and QVariant, as explained, but in both cases I got runtime errors.It sounds strange that I have to register it with Q_DECLARE_METATYPE(QMediaPlaylist*) considering that it's a QT Object. Could it be a bug as happened here with QAbstractListItem?
https://bugreports.qt.io/browse/QTBUG-68411Despite of all, I'm still getting error Unknown method return type: QMediaPlaylist*..
I'm thinking to alternative approaches like passing QStringlist of Qurl, which is supported by QML, instead of QMediaplayist.Best Regards,
Davide Brunelli