Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*)
Forum Updated to NodeBB v4.3 + New Features

Pass a Playlist from C++ to QML (error Unable to assign [undefined] to ::QDeclarativeAudio*)

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
9 Posts 3 Posters 925 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    davidino
    wrote on last edited by davidino
    #1

    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

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      @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().

      (Z(:^

      1 Reply Last reply
      0
      • D Offline
        D Offline
        davidino
        wrote on last edited by davidino
        #3

        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

        1 Reply Last reply
        0
        • sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #4

          @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*).

          (Z(:^

          1 Reply Last reply
          0
          • D Offline
            D Offline
            davidino
            wrote on last edited by davidino
            #5

            @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

            KroMignonK 1 Reply Last reply
            0
            • D davidino

              @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

              KroMignonK Offline
              KroMignonK Offline
              KroMignon
              wrote on last edited by KroMignon
              #6

              @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
                  }
              }
              

              It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

              1 Reply Last reply
              0
              • D Offline
                D Offline
                davidino
                wrote on last edited by davidino
                #7

                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

                KroMignonK 1 Reply Last reply
                0
                • D davidino

                  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

                  KroMignonK Offline
                  KroMignonK Offline
                  KroMignon
                  wrote on last edited by KroMignon
                  #8

                  @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

                  It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                  1 Reply Last reply
                  0
                  • D Offline
                    D Offline
                    davidino
                    wrote on last edited by davidino
                    #9

                    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-68411

                    Despite 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

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved