Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Creating QTableView dynamically / Creating new playlists



  • So I have an already created (not my code) QTableView which is used for a media playlist, it is already up and running. The problem is that the app originally was designed to have only playlist, this requirement have changed and now they want to make it possible for the user to create as many playlists as they want to.

    I have been tasked to make this possible, the requirement is that this is done with a dropdown at the top to choose a playlist. I have googled for this but few results came up and none was what I need.
    I want to know if I can do this working with the already existing QTableView/Playlist without having to re-write the whole thing.

    Is this possible? If it is, how can I do it?

    I am also thinking about grouping the tableview rows, sort of like QTreeView, can't find a way to do it.

    Worth noting that I am a beginner and this is my first time coming in contact with the model/view approach of doing things, I was comfortably using only QTableWidget


  • Lifetime Qt Champion

    Hi,

    How are these playlist created ?
    Where are they stored ?

    The most simple here is to list these playlist in your combo box and once a user selects its, reload your QTableWidget content based on it.


  • Lifetime Qt Champion

    Hi
    using a QTableView + std. item model is not that different from using
    the QTableWidget. The QTableWidget just has its model internally and with the view,
    you have it externally.

    so as @SGaist says, how is the single playlist handled now ?
    You need to be able to have a list of playlists or something of that sorts.



  • @hbatalha
    Since it sounds like you already have a QTableView, I assume it's using your own model (what is its model type?). I don't see any point or need to change to a QTableWidget and copy data. When the user selects in the combo box, just change your model to the new playlist and do a begin/endResetModel(), or simlar.



  • @SGaist @mrjj

    How are these playlist created ?
    Where are they stored ?

    I am not sure how to answer these questions. As I said I am trying to figure out the code since this is my first contact with model/view programming.

    @SGaist @JonB

    reload your QTableWidget content based on it.

    No, I stated QTableWidget just to tell my background regarding Qt. I will have to keep using QTableView.



  • @mrjj said in Creating QTableView dynamically / Creating new playlists:

    You need to be able to have a list of playlists or something of that sorts.

    I am experimenting having a list of QTableView*

    @JonB said in Creating QTableView dynamically / Creating new playlists:

    When the user selects in the combo box, just change your model to the new playlist and do a begin/endResetModel()

    Any example?


  • Lifetime Qt Champion

    There's not need to have several QTableView. As was already suggested, you should update the model.

    One question that you did not answer: how are you currently handling your playlist ?

    You might also be interested in the Qt Media Player example.



  • @SGaist said in Creating QTableView dynamically / Creating new playlists:

    As was already suggested, you should update the model.

    So I should have a list of models?

    @SGaist said in Creating QTableView dynamically / Creating new playlists:

    how are you currently handling your playlist ?

    I am still not sure how to respond? Should I post the code from PlaylistModel.h?


  • Lifetime Qt Champion

    @hbatalha said in Creating QTableView dynamically / Creating new playlists:

    @SGaist said in Creating QTableView dynamically / Creating new playlists:

    As was already suggested, you should update the model.

    So I should have a list of models?

    Why ? You can replace the content of a model.

    @hbatalha said in Creating QTableView dynamically / Creating new playlists:

    @SGaist said in Creating QTableView dynamically / Creating new playlists:

    how are you currently handling your playlist ?

    I am still not sure how to respond? Should I post the code from PlaylistModel.h?

    That can be a good start.



  • @SGaist said in Creating QTableView dynamically / Creating new playlists:

    That can be a good start.

    #ifndef PLAYLISTMODEL_H
    #define PLAYLISTMODEL_H
    
    #include <QAbstractTableModel>
    #include <qmimedata.h>
    #include <QStringList>
    #include "mltcontroller.h"
    #include "MltPlaylist.h"
    
    #define kDetailedMode "detailed"
    #define kIconsMode "icons"
    #define kTiledMode "tiled"
    
    class PlaylistModel : public QAbstractTableModel
    {
        Q_OBJECT
    public:
        enum ViewMode {
            Invalid,
            Detailed,
            Tiled,
            Icons,
        };
    
        enum Columns {
            COLUMN_INDEX = 0,
            COLUMN_THUMBNAIL,
            COLUMN_RESOURCE,
            COLUMN_IN,
            COLUMN_DURATION,
            COLUMN_START,
            COLUMN_DATE,
            COLUMN_COUNT
        };
    
        enum Fields {
            FIELD_INDEX = Qt::UserRole,
            FIELD_THUMBNAIL,
            FIELD_RESOURCE,
            FIELD_IN,
            FIELD_DURATION,
            FIELD_START,
            FIELD_DATE,
        };
    
        static const int THUMBNAIL_WIDTH = 80;
        static const int THUMBNAIL_HEIGHT = 45;
    
        explicit PlaylistModel(QObject *parent = 0);
        ~PlaylistModel();
        int rowCount(const QModelIndex& parent = QModelIndex()) const;
        int columnCount(const QModelIndex& parent = QModelIndex()) const;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
        Qt::DropActions supportedDropActions() const;
        bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex());
        bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
        bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
        void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
        Qt::ItemFlags flags(const QModelIndex &index) const;
        QStringList mimeTypes() const;
        QMimeData *mimeData(const QModelIndexList &indexes) const;
        bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
        QModelIndex incrementIndex(const QModelIndex& index) const;
        QModelIndex decrementIndex(const QModelIndex& index) const;
        QModelIndex createIndex(int row, int column) const;
        void createIfNeeded();
        void showThumbnail(int row);
        void refreshThumbnails();
        Mlt::Playlist* playlist() { return m_playlist; }
        void setPlaylist(Mlt::Playlist& playlist);
        void setInOut(int row, int in, int out);
    
        ViewMode viewMode() const;
        void setViewMode(ViewMode mode);
    
    signals:
        void created();
        void cleared();
        void closed();
        void modified();
        void loaded();
        void dropped(const QMimeData *data, int row);
        void moveClip(int from, int to);
        void inChanged(int in);
        void outChanged(int out);
    
    public slots:
        void clear();
        void load();
        void append(Mlt::Producer&, bool emitModified = true);
        void insert(Mlt::Producer&, int row);
        void remove(int row);
        void update(int row, Mlt::Producer& producer, bool copyFilters = false);
        void updateThumbnails(int row);
        void appendBlank(int frames);
        void insertBlank(int frames, int row);
        void close();
        void move(int from, int to);
    
    private:
        Mlt::Playlist* m_playlist;
        int m_dropRow;
        ViewMode m_mode;
        QList<int> m_rowsRemoved;
    };
    
    #endif // PLAYLISTMODEL_H
    

  • Lifetime Qt Champion

    From the looks of it, you would only need to call setPlayList that should trigger a reset of your model and you should be good to go as this will trigger the update of your UI.



  • @SGaist said in Creating QTableView dynamically / Creating new playlists:

    you would only need to call setPlayList that should trigger a reset of your model and you should be good to go

    How will the files added in a playlist be saved when I switched back to a playlist?


  • Lifetime Qt Champion

    Since you will have several playlist, you'll likely have them stored using QList or QVector.

    There's no deletion happening, you should "exchange" the current playlist with the one selected.



  • @SGaist said in Creating QTableView dynamically / Creating new playlists:

    Since you will have several playlist

    So I will have a QVector<Mlt::Playlist*>?



  • @hbatalha
    Yes, QVector<Mlt::Playlist*> or QList<Mlt::Playlist*> would be fine.

    If you are feeling adventurous, you might even opt for a QMap<QString, Mlt::Playlist*>. If your playlists are simply distinguished by their name in the droplist, saves you having to look it up!



  • @JonB

    It doesn't work, when I switched a playlist it reload with a empty playlist.
    The code:

    void PlaylistDock::on_addButton_clicked()
    {
        Mlt::Playlist* playlist = new Mlt::Playlist;
    
        m_playlists.append(playlist);
    
        ui->comboBox->addItem("Playlist " + QString::number(i++));
    }
    
    void PlaylistDock::on_comboBox_currentIndexChanged(int)
    { 
        m_model.setPlaylist(*m_playlists.at(ui->comboBox->currentIndex()));
    }
    

  • Lifetime Qt Champion

    @hbatalha said in Creating QTableView dynamically / Creating new playlists:

    void PlaylistDock::on_addButton_clicked()
    {
    Mlt::Playlist* playlist = new Mlt::Playlist;

    m_playlists.append(playlist);
    
    ui->comboBox->addItem("Playlist " + QString::number(i++));
    

    }

    You are creating a new empty playlist so that's normal that your model is empty after you set.



  • @hbatalha said in Creating QTableView dynamically / Creating new playlists:

    void PlaylistDock::on_comboBox_currentIndexChanged(int)
    {
    m_model.setPlaylist(*m_playlists.at(ui->comboBox->currentIndex()));
    }

    @SGaist

    You are creating a new empty playlist

    I mean when I switch playlist.





  • @JonB said in Creating QTableView dynamically / Creating new playlists:

    @hbatalha
    As I wrote earlier, you need to use QAbstractItemModel::beginResetModel() when you change models. See e.g. https://stackoverflow.com/questions/14756645/how-to-reset-model-in-qt.

    The playlist model inherits from QAbstractTableModel as shown in the code above



  • @hbatalha
    Yes, and what does that inherit from...?





  • @hbatalha said in Creating QTableView dynamically / Creating new playlists:

    beginResetModel is protected. https://doc.qt.io/qt-5/qabstractitemmodel.html#beginResetModel

    You wrote earlier

    The playlist model inherits from QAbstractTableModel as shown in the code above

    So that means you can call protected methods, doesn't it?

    If you have some aversion to resetting the model, you can probably delete all existing rows and insert all new rows if you really wish.



  • @JonB said in Creating QTableView dynamically / Creating new playlists:

    So that means you can call protected methods, doesn't it?

    That is what I expected, I am finding strange that the compiler is complaining that it is protected.



  • @hbatalha
    If you are trying to go

    m_model.beginResetModel();
    m_model.setPlaylist(*m_playlists.at(ui->comboBox->currentIndex()));
    m_model.endResetModel();
    

    you cannot do that on protected methods. You must call them in PlaylistModel::setPlaylist().



  • @JonB said in Creating QTableView dynamically / Creating new playlists:

    you cannot do that on protected methods. You must call them in PlaylistModel::setPlaylist().

    yeah I fixed that.

    But still, the playlist will be empty when I switch. I must be missing something.


  • Lifetime Qt Champion

    Where/when are you putting stuff in that playlist ?
    You only show that you created it but at no point you seem to put anything in it.



  • @SGaist There is a drop event where the files added to the playlist will be at the same time added to the playlist vector current element (m_playlists.at(ui->comboBox->currentIndex())).

    But I since discovered that the Mlt::Playlist doesn't allow copying. When I set a playlist (m_model.setPlaylist(*m_playlists.at(ui->comboBox->currentIndex())); ) the m_model.playlist() takes omership of all the elements in the playlist and when I switch to another playlist they get deleted. Here is the Mlt::Playlist interface:

    class MLTPP_DECLSPEC Playlist : public Producer
    	{
    		private:
    			mlt_playlist instance;
    		public:
    			Playlist( );
    			Playlist( Profile& profile );
    			Playlist( Service &playlist );
    			Playlist( Playlist &playlist );
    			Playlist( mlt_playlist playlist );
    			virtual ~Playlist( );
    			virtual mlt_playlist get_playlist( );
    			mlt_producer get_producer( );
    			int count( );
    			int clear( );
    			int append( Producer &producer, int in = -1, int out = -1 );
    			int blank( int out );
    			int blank( const char *length );
    			int clip( mlt_whence whence, int index );
    			int current_clip( );
    			Producer *current( );
    			ClipInfo *clip_info( int index, ClipInfo *info = NULL );
    			static void delete_clip_info( ClipInfo *info );
    			int insert( Producer &producer, int where, int in = -1, int out = -1 );
    			int remove( int where );
    			int move( int from, int to );
    			int reorder( const int *indices );
    			int resize_clip( int clip, int in, int out );
    			int split( int clip, int position );
    			int split_at( int position, bool left = true );
    			int join( int clip, int count = 1, int merge = 1 );
    			int mix( int clip, int length, Transition *transition = NULL );
    			int mix_in( int clip, int length );
    			int mix_out( int clip, int length );
    			int mix_add( int clip, Transition *transition );
    			int repeat( int clip, int count );
    			Producer *get_clip( int clip );
    			Producer *get_clip_at( int position );
    			int get_clip_index_at( int position );
    			bool is_mix( int clip );
    			bool is_blank( int clip );
    			bool is_blank_at( int position );
    			void consolidate_blanks( int keep_length = 0 );
    			Producer *replace_with_blank( int clip );
    			void insert_blank( int clip, int out );
    			void pad_blanks( int position, int length, int find = 0 );
    			int insert_at( int position, Producer *producer, int mode = 0 );
    			int insert_at( int position, Producer &producer, int mode = 0 );
    			int clip_start( int clip );
    			int clip_length( int clip );
    			int blanks_from( int clip, int bounded = 0 );
    			int remove_region( int position, int length );
    	};
    

    And here is the PlaylistModel::setPlaylist:

    void PlaylistModel::setPlaylist(Mlt::Playlist& playlist)
    {
        if (playlist.is_valid()) {
            if (m_playlist) {
                if (rowCount()) {
                    beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
                    m_playlist->clear();
                    endRemoveRows();
                }
                delete m_playlist;
            }
            m_playlist = new Mlt::Playlist(playlist);
            if (!m_playlist->is_valid()) {
                delete m_playlist;
                m_playlist = 0;
                return;
            }
            if (m_playlist->count() > 0) {
                beginInsertRows(QModelIndex(), 0, m_playlist->count() - 1);
                endInsertRows();
            }
            // do not let opening a clip change the profile!
            MLT.profile().set_explicit(true);
            if (Settings.playerGPU() && Settings.playlistThumbnails() != "hidden")
                refreshThumbnails();
            emit loaded();
        }
    }
    

    I tried creating vector that hold the actual filenames and then every time I switch to playlist I would clear the m_model.playlist and add the new files but that process is slow, specially when you have multiple files, so that was not not a option.

    I can't think of any other way now, I looking into using a vector of models.


  • Lifetime Qt Champion

    Why are you trying to pass reference around rather than pointers ?



  • @SGaist I am not, that is how Mlt::Playlist works.


  • Lifetime Qt Champion

    You do that in your PlaylistModel. You are passing everything by reference although you store all your PlayLists as pointers. Hence fail to see why you are using references in you model interface. Also why are you creating so many copies of your PlayList objects ?



  • @SGaist said in Creating QTableView dynamically / Creating new playlists:

    You are passing everything by reference although you store all your PlayLists as pointers.

    That's how I found the code. However I changed that, but it crashes every time I switch back to a playlist:

    void PlaylistModel::setPlaylist(Mlt::Playlist *playlist)
    {
        if (playlist->is_valid())
        {
            if (m_playlist)
            {
                if (rowCount())
                {
                    beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
    //                m_playlist->clear();
                    endRemoveRows();
                }
                // delete m_playlist;
            }
            m_playlist = playlist; // crashes here
            if (!m_playlist->is_valid())
            {
                // delete m_playlist;
                m_playlist = 0;
                return;
            }
            if (m_playlist->count() > 0)
            {
                beginInsertRows(QModelIndex(), 0, m_playlist->count() - 1);
                endInsertRows();
            }
            // do not let opening a clip change the profile!
            MLT.profile().set_explicit(true);
            if (Settings.playerGPU() && Settings.playlistThumbnails() != "hidden")
                refreshThumbnails();
            emit loaded();
        }
    }
    ``

  • Lifetime Qt Champion

    One thing you should do: call begin/endResetModel when you replace the playlist.

    Then, the debugger is your friend.



  • @SGaist That did it, can't thank you enough.

    Thank you all for the time and help.


Log in to reply