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

QTableview won't update from model



  • Hey , I am writing a setlist application in Qt C++ and have come across a couple of things I can't seem to find out how to do (Excuse me for messy code and if I am missing some basics as I am not a programmer! Any tips/criticism are/is welcome!);
    So I have a song list in a SQLite DB which is shown in a QTableview inside a tab in a QTabWidget. I can select songs from this list and create a setlist. This set list has some settings and basically breaks songs into a number of sections and sorts them depending on settings. It then creates new tables in the DB and new tabs ( with a QTableview in each one) with each section to show the data.
    I have enabled drag and drop of the headers to allow me drag and drop songs in the setlist. (I couldn't get the Qt Drag and drop function to work properly after a couple of months of trying...I will do another post about this I guess...unless someone can point me to a tutorial where it is done!!) Anyway, I allow the headers to be dragged and dropped (songlistView->verticalHeader()->setSectionsMovable(true);) and I manually change an index and update the table in the DB. I connect the &QHeaderView::sectionMoved signal to a rowDragged slot when I create the setlist QTableViews.
    This all works perfectly when I create the setlist in the program, QTableView updates perfectly when I call the (modelname)->select function at the end of the rowDragged slot.
    The problem is ; In the constructor I load up all the tabs from the default setlists (created on a previous run of the program) and the same function doesn't update the QTableView when I drag a header!
    Can anyone spot any obvious errors or see what I am doing wrong?

    Note the signal emitted at the end is a hack to redisplay the tab to get around this issue (It works but it's a hack and I'm sure I'm missing something obvious)

    Apologies for the large amount of code but wanted to add all relevant code....Any tips welcome...

    RowDragged Slot

    void SQLiteDB::rowDraggedSlotSec_1(int logicalIndex, int oldVisualIndex, int newVisualIndex)
    {
        qDebug() << logicalIndex;
        qDebug() << oldVisualIndex;
        qDebug() << newVisualIndex;
    
        QStringList columns = getTableInfo(m_setlistTableNameSec_1);
    
    // Debug start****
        //QList<QStringList> draggedRow = readFromDatabase(m_setlistTableNameSec_1,columns, logicalIndex,1);
        //logical index is zero based
        //qDebug() << draggedRow;
    
    //    QMessageBox msgBox;
    //    msgBox.setWindowTitle("Song Dragged");
    //    msgBox.setIcon(QMessageBox::Information);
    //    msgBox.setText(QString("Song %1 Dragged to %2 from Position %3")
    //                   .arg(logicalIndex+1).arg(newVisualIndex+1).arg(oldVisualIndex+1));
    //    msgBox.setStandardButtons(QMessageBox::Ok);
    //    int reply = msgBox.exec();
    // Debug end ****
    
        changePositionIndexes(m_setlistTableNameSec_1,newVisualIndex+1, oldVisualIndex+1);
    
        // This updates the tableview - Well it actually only updates the tableview when a setlist
        // has been created.
        // It doesn't work when I load up the previously created setlist in the mainwindow constructor
    
        m_setlistModelSec_1->select();
    
        emit rowDragDatabaseUpdated(m_setlistTableNameSec_1);
    
    }
    

    Mainwindow Constructor

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
    
        m_tabWidget = new QTabWidget;
        connect(m_tabWidget,&QTabWidget::tabCloseRequested,this,&MainWindow::closeTab);
        connect(songDB,&SQLiteDB::rowDragDatabaseUpdated,this,&MainWindow::updateTableView);
    
    
        QWidget::showMaximized(); // To show maximised window when mainwindow is run
    
        songDB->openDefaultDatabase();
    
    
    
        // These lines have to go here or else the table won't populate (before disabling UI elements and maximising widget)
        QSqlRelationalTableModel *model1 = new QSqlRelationalTableModel;
        songDB->setSongListModel(model1); //Set this as it used in dataChanged slot for updates of database when editing fomr the tableview
        setupAndDisplayTableView(model1,SONGLIST_TABLE,QList<int>(), SONG_INDEX, INDEXZERO);
    
    
        if (songDB->checkDBforTable(NEWSONGLIST_TABLE))
        {
            QSqlRelationalTableModel *model2 = new QSqlRelationalTableModel;
            songDB->setNewSongListModel(model2);
            setupAndDisplayTableView(model2,NEWSONGLIST_TABLE,QList<int>(), DATE_ADDED_INDEX,INDEXZERO);
    
    
        }
        if (songDB->checkDBforTable(SETLIST_TABLE))
        {
            QSqlRelationalTableModel *model3 = new QSqlRelationalTableModel;
            songDB->setSetlistModel(model3);
            setupAndDisplayTableView(model3,SETLIST_TABLE,QList<int>(), ID_INDEX + 1,INDEXZERO); // adjusted by 1 for Position column
        }
    
        //Setlist Section 1
        QString tablename;
        tablename = songDB->checkDBforTableContainsString(SETLIST_TABLE_1);
    
    
        if (tablename != "")
        {
            songDB->setSetlistTableNameSec_1(tablename);
            QSqlRelationalTableModel *model4 = new QSqlRelationalTableModel; // Do I need to have a new variable name, could I not use same as above because it's scope is only in th if statement?
            songDB->setSetlistModelSec_1(model4); //What does this actually do??
            setupAndDisplayTableView(model4,tablename,QList<int>(), ID_INDEX+1,INDEXZERO); // adjusted by 1 for Position column
        }
    
        //Setlist Section 2
        tablename = songDB->checkDBforTableContainsString(SETLIST_TABLE_2);
        if (tablename != "")
        {
            songDB->setSetlistTableNameSec_2(tablename);
            QSqlRelationalTableModel *model5 = new QSqlRelationalTableModel; // Do I need to have a new variable name, could I not use same as above because it's scope is only in th if statement?
            songDB->setSetlistModelSec_2(model5); //What does this actually do??
            setupAndDisplayTableView(model5,tablename,QList<int>(), ID_INDEX+1,INDEXZERO); // adjusted by 1 for Position column
        }
    
        //Setlist Section 3
        tablename = songDB->checkDBforTableContainsString(SETLIST_TABLE_3);
        if (tablename != "")
        {
            songDB->setSetlistTableNameSec_3(tablename);
            QSqlRelationalTableModel *model6 = new QSqlRelationalTableModel; // Do I need to have a new variable name, could I not use same as above because it's scope is only in th if statement?
            songDB->setSetlistModelSec_3(model6); //What does this actually do??
            setupAndDisplayTableView(model6,tablename,QList<int>(), ID_INDEX+1,INDEXZERO); // adjusted by 1 for Position column
        }
    
        //Setlist Section 4
        tablename = songDB->checkDBforTableContainsString(SETLIST_TABLE_4);
        if (tablename != "")
        {
            songDB->setSetlistTableNameSec_4(tablename);
            QSqlRelationalTableModel *model7 = new QSqlRelationalTableModel; // Do I need to have a new variable name, could I not use same as above because it's scope is only in th if statement?
            songDB->setSetlistModelSec_4(model7); //What does this actually do??
            setupAndDisplayTableView(model7,tablename,QList<int>(), ID_INDEX+1, INDEXZERO); // adjusted by 1 for Position column
        }
    
        //Setlist Section 5
        tablename = songDB->checkDBforTableContainsString(SETLIST_TABLE_5);
        if (tablename != "")
        {
            songDB->setSetlistTableNameSec_5(tablename);
            QSqlRelationalTableModel *model8 = new QSqlRelationalTableModel; // Do I need to have a new variable name, could I not use same as above because it's scope is only in th if statement?
            songDB->setSetlistModelSec_5(model8); //What does this actually do??
            setupAndDisplayTableView(model8,tablename,QList<int>(), ID_INDEX+1, INDEXZERO); // adjusted by 1 for Position column
        }
        // Set songlist as current tab
        m_tabWidget->setCurrentIndex(0);
    
    }
    
    

    Set Up And Display function

    void MainWindow::setupAndDisplayTableView(QSqlRelationalTableModel *model, QString tableName, QList<int> columnsToHide, int sortByColumn, int tabIndex)
    {
    
        QString tableViewName;
        QString tabName;
    
        if (tableName == SONGLIST_TABLE)
        {
            tableViewName = MAIN_TABLEVIEW_NAME;
            tabName = SONGLIST_TAB_NAME;
        }
        else if (tableName == NEWSONGLIST_TABLE)
        {
            tableViewName = NEWSONG_TABLEVIEW_NAME;
            tabName = NEWLIST_TAB_NAME;
        }
        else if (tableName == ARCHIVEDSONGS_TABLE)
        {
            tableViewName = ARCHIVE_TABLEVIEW_NAME;
            tabName = ARCHIVED_TAB_NAME;
        }
        else if (tableName.contains(SETLIST_TABLE))
        {
            tableViewName = tableName;
            tabName = tableName;//SETLIST_TAB_NAME;
    
    
        }
    
        //How to do the Relational Model tableview
        //1. Define a SqlRelationalTableModel
        //2. Initialise the model
        //3. Define the TableView
        //4. Create the Tableview using the model
    
       // Just put the qtableview into main window
    //     QGridLayout *layout = new QGridLayout;
    //     songDB->initializeSonglistModel(songlistModel, "songlist");
    //     QTableView *songlistView = new QTableView;
    //     songlistView = songDB->createSonglistView(QObject::tr("Relational Table Model"),songlistModel);
    //     layout->addWidget(songlistView,0,0);
    //     QWidget *window = new QWidget();
    //     window->setLayout(layout);
    //     setCentralWidget(window);
    
    //    QSqlRelationalTableModel *localSonglistModel = new QSqlRelationalTableModel;
    //    localSonglistModel = songDB->getSonglistModel();
    
    
        //songDB->initializeSonglistModelOld(model, tableName);
    
        songDB->initializeSonglistModel(model, tableName);
    
    
        QTableView *songlistView = new QTableView;
    
    //    findChildren
    
    
    
    
        songlistView = songDB->createSonglistView(tableViewName,model,columnsToHide ,sortByColumn);
    
    
    
    
        // Relational Data table procedure
        // 1. Create tables in DB
        // 2. Define the Model
        // 4. Initialise the model ie set up the realtions and the column names
        // 5. Define the view
        // 6. Create the view
        // 7. Add widget (view) to layout
        // 8. create 'window' widget
        // 9. set'window' widget layout
        // 10. make central widget
    
        // Tab Version
    
        // To create a QTablewidget in a tab
        //1. Create a table
        //2. Create a layout
        //3. Create a TabWidget
        //4. Create a new tab (as a widget) ie QWidget newTab = *QWidget;
        //5. Add the tabel to the layout
        //6. Set the new tab layout
        //7. Add the new tab to the Tab widget, with the tab name
        //8. Set the Tab Widget as central widget
    
        QGridLayout *layout = new QGridLayout;
    //         QTabWidget *newTabWidget = new QTabWidget;
        m_tabWidget->setObjectName(TABWIDGET_NAME);
        m_tabWidget->setTabsClosable(true);
        qDebug() << m_tabWidget->objectName();
        QWidget *newTab = new QWidget;
    
        layout->addWidget(songlistView);
    
        newTab->setLayout(layout);
        if (tabIndex == 0)
        {
            m_tabWidget->addTab(newTab, tabName);
        }
        else
        {
            m_tabWidget->insertTab(tabIndex,newTab,tabName);
        }
        m_tabWidget->setCurrentWidget(newTab);
    
        newTab->setObjectName(tableName);
        qDebug() << newTab->objectName();
    
        setCentralWidget(m_tabWidget);
    
    }
    

    Initialise Model

    void SQLiteDB::initializeSonglistModel(QSqlRelationalTableModel *songlistModel, QString tableName)
    {
        //Extra added for setlist where column names are not known but should also work for all views
        QString relationalTableName;
        QString columnIDHeader;
        QString columnHeader;
        int flag;
    
        //Store the columnHeader string in a list while looping thru the first loop below
        //so the headers data can be set in another loop after relations are set
    
    
        QList<QStringList> tableData = readFromDatabase(tableName);
        QStringList columnHeaders = getTableInfo(tableName);
    
        songlistModel->setTable(tableName);
        songlistModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
    
    
        for (int i=0;i<columnHeaders.size();i++)
        {
            flag = 0;
               if (columnHeaders[i] == ARTIST_COLUMN_HEADER)
               {
                   relationalTableName = ARTIST_TABLE;
                   columnIDHeader = ARTIST_COLUMN_ID_HEADER;
                   columnHeader = ARTIST_COLUMN_HEADER;
                   flag = 1;
               }
               else if (columnHeaders[i] == TUNING_COLUMN_HEADER)
               {
                   relationalTableName = TUNING_TABLE;
                   columnIDHeader = TUNING_COLUMN_ID_HEADER;
                   columnHeader = TUNING_COLUMN_HEADER;
                   flag = 1;
               }
               else if (columnHeaders[i] == VOCAL_COLUMN_HEADER)
               {
                   relationalTableName = VOCAL_TABLE;
                   columnIDHeader = VOCAL_COLUMN_ID_HEADER;
                   columnHeader = VOCAL_COLUMN_HEADER;
                   flag = 1;
               }
               else if (columnHeaders[i] == PERC_COLUMN_HEADER)
               {
                   relationalTableName = PERC_TABLE;
                   columnIDHeader = PERC_COLUMN_ID_HEADER;
                   columnHeader = (PERC_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == KEY_COLUMN_HEADER)
               {
                   relationalTableName = KEY_TABLE;
                   columnIDHeader = KEY_COLUMN_ID_HEADER;
                   columnHeader = (KEY_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == RELEASE_YEAR_COLUMN_HEADER)
               {
                   relationalTableName = RELEASE_YEAR_TABLE;
                   columnIDHeader = RELEASE_YEAR_COLUMN_ID_HEADER;
                   columnHeader = (RELEASE_YEAR_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == YEAR_COLUMN_HEADER)
               {
                   relationalTableName = YEAR_TABLE;
                   columnIDHeader = YEAR_COLUMN_ID_HEADER;
                   columnHeader = (YEAR_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == SCROLL_SPEED_COLUMN_HEADER)
               {
                   relationalTableName = SCROLL_SPEED_TABLE;
                   columnIDHeader = SCROLL_SPEED_COLUMN_ID_HEADER;
                   columnHeader = (SCROLL_SPEED_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == BPM_COLUMN_HEADER)
               {
                   relationalTableName = BPM_TABLE;
                   columnIDHeader = BPM_COLUMN_ID_HEADER;
                   columnHeader = (BPM_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == LOOP_COLUMN_HEADER)
               {
                   relationalTableName = LOOP_TABLE;
                   columnIDHeader = LOOP_COLUMN_ID_HEADER;
                   columnHeader = (LOOP_COLUMN_HEADER);
                   flag = 1;
               }
               else if (columnHeaders[i] == DATE_ADDED_COLUMN_HEADER)
               {
                   relationalTableName = DATE_ADDED_TABLE;
                   columnIDHeader = DATE_ADDED_COLUMN_ID_HEADER;
                   columnHeader = (DATE_ADDED_COLUMN_HEADER);
                   flag = 1;
               }
    
               if (flag==1)
               {
                   songlistModel->setRelation(i, QSqlRelation(relationalTableName, columnIDHeader, columnHeader));
               }
    
        }
    
    //    songlistModel->setHeaderData(0, Qt::Horizontal, ITEM_COLUMN_HEADER);
    //    songlistModel->setHeaderData(1, Qt::Horizontal, SONG_COLUMN_HEADER);
    
        for (int i=0;i<columnHeaders.size();i++)
        {
            songlistModel->setHeaderData(i, Qt::Horizontal, columnHeaders[i]);
        }
    
        songlistModel->select();
    }
    
    

    Create song list view

    QTableView *SQLiteDB::createSonglistView(const QString &title, QSqlTableModel *model, QList<int> columnsToHide,int sortByColumn)
    {
        CustomDelegate *delegate = new CustomDelegate();
        QTableView *songlistView = new QTableView;
    
    
        QMetaObject::Connection result = QObject::connect(model, &QSqlTableModel::dataChanged,this, &SQLiteDB::dataChangedSlot);
        qDebug() << result;
    
        songlistView->setObjectName(title);
        qDebug() << songlistView->objectName();
        songlistView->setSortingEnabled(true);
        songlistView->setAlternatingRowColors(true);
        //songlistView->setItemDelegate(new QSqlRelationalDelegate(this));
        songlistView->setItemDelegate(delegate);
    
    
        if (title.contains(SETLIST_TABLE))
        {
            //Drag and Drop by column header - not perfect because it's just a change to the view and
            //not the model or data underneath the view
            //http://apocalyptech.com/linux/qt/qtableview/
            songlistView->verticalHeader()->setSectionsMovable(true);
            songlistView->verticalHeader()->setDragEnabled(true);
            songlistView->verticalHeader()->setDragDropMode(QAbstractItemView::InternalMove);
            //songlistView->sortByColumn(4,Qt::AscendingOrder);
            songlistView->setSortingEnabled(false);
    
    
            songlistView->setSelectionBehavior(QAbstractItemView::SelectRows);
            songlistView->setSelectionMode(QAbstractItemView::SingleSelection);
    
            //songlistView->setObjectName(SETLIST_TABLEVIEW_NAME);
    
            //Connect signal slot to findout when a row has been dragged
    
            // m_setlistTableNameSec_1
            if (songlistView->objectName() == m_setlistTableNameSec_1)
            {
                result = QObject::connect(songlistView->verticalHeader(), &QHeaderView::sectionMoved,this, rowDraggedSlotSec_1);
                qDebug() << result;
            }
            // m_setlistTableNameSec_2
            if (songlistView->objectName() == m_setlistTableNameSec_2)
            {
                result = QObject::connect(songlistView->verticalHeader(), &QHeaderView::sectionMoved,this, rowDraggedSlotSec_2);
                qDebug() << result;
            }
            // m_setlistTableNameSec_3
            if (songlistView->objectName() == m_setlistTableNameSec_3)
            {
                result = QObject::connect(songlistView->verticalHeader(), &QHeaderView::sectionMoved,this, rowDraggedSlotSec_3);
                qDebug() << result;
            }
            // m_setlistTableNameSec_4
            if (songlistView->objectName() == m_setlistTableNameSec_4)
            {
                result = QObject::connect(songlistView->verticalHeader(), &QHeaderView::sectionMoved,this, rowDraggedSlotSec_4);
                qDebug() << result;
            }
            // m_setlistTableNameSec_5
            if (songlistView->objectName() == m_setlistTableNameSec_5)
            {
                result = QObject::connect(songlistView->verticalHeader(), &QHeaderView::sectionMoved,this, rowDraggedSlotSec_5);
                qDebug() << result;
            }
    
    
        }
    
        //Set up Model
        songlistView->setModel(model);
    
        //These need to come after the model is set for the tableview as they need the data to work.
        songlistView->resizeColumnsToContents();
        songlistView->sortByColumn(sortByColumn-1,Qt::AscendingOrder);
        songlistView->setAlternatingRowColors(true);
    
        if (!columnsToHide.isEmpty())
        {
    //        int columnIndex;
            for (int i=0;i<columnsToHide.size();i++)
            {
    //            columnIndex = columnsToHide[i].toInt();
                songlistView->setColumnHidden(columnsToHide[i], true);
            }
        }
    
        // This line will stretch the column widths so that the tableview fills the width of the screen
        // songlistView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    
        return songlistView;
    }
    

  • Lifetime Qt Champion

    Hi,

    Not a direct answer yet but one thing you should do to simplify your code would be to create one widget that represent one SetList, that way you'll have one point of implementation for it rather that repetitive code.



  • @SGaist said in QTableview won't update from model:

    Hi,

    Not a direct answer yet but one thing you should do to simplify your code would be to create one widget that represent one SetList, that way you'll have one point of implementation for it rather that repetitive code.

    Sorry , don't really understand your answer. I already have a separate QTableview and Tab inside QTabwidget for each setlist.
    Thanks for reply btw!


Log in to reply