[SOLVED]How to update the TableView in QML or C++?



  • I am coding a program combining QML and C++. The TableView shows a column of a table. I can add or delete the record correctly, but I can not update the TableView, which always shows the content before adding or deleting.

    How to update the TableView?

    PS: I do not know whether it is good choice to code with QML and C++ instead of QWidget directly. At least by now, it keeps frustrating me.


  • Moderators

    Hi @CoderJeff,
    Assuming you are using QAbstractTableModel or QAbstractItemModel as C++ model with QML, to update the view, use setData to update the model and then fire the dataChanged signal with appropriate index to notify the view about the changes.
    It would be helpful if you post some code.

    I do not know whether it is good choice to code with QML and C++ instead of QWidget directly. At least by now, it keeps frustrating me.

    It totally depends upon your requirements.



  • @p3c0

    Actually, I am using QSqlTableModel.

    class MySqlModel : public QSqlTableModel
    {
    ......
    void addRecord(field1,field2, ..., fieldN);
    void deleteRecord();
    void updateRecord(field1, field2, ..., fieldN);
    ......
    }

    The TableView in QML has only one column.

    After running addRecord() and deleteRecord(), I want to update the content of TableView. But I do not know how to do it. The following is a part of my code.

    main() {
    //other code
    ......

    MySqlModel *model = new MySqlModel;
    model->QSqlQueryModel::setQuery("SELECT FieldName1 FROM Table");

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty ("SQQL", model);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    //other code
    ......
    }

    In the qml:
    TableView{
    //other code
    ......

    model: SQQL

    //other code
    ......
    }

    How to use setData and dataChanged to connect MySqlModel in C++ and TableView in QML?


  • Moderators

    @CoderJeff Are addRecord, updateRecord accessible from QML ?
    Once you have called say addRecord(int field1) you need to call setData() after it to update the model and dataChanged() with proper index.
    An example from one of my code:

    void PicModel::updateTitle(int row, int col, QString title)
    {
        QModelIndex modelIndex = this->index(row,col);
        setData(modelIndex, title, TitleRole);
        return;
    }
    
    bool PicModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (role == TitleRole) {
            m_data[index.row()].m_title = value.toString(); //Just a list which holds 'struct' of pics info
        }
        emit dataChanged(index, index);
        return true;
    }
    

    So from QML updateTitle is invoked with parameters (row and title) and the rest code updates the QList (data holder) and then updates the view.



  • @p3c0 said:

    Are addRecord, updateRecord accessible from QML ?

    Yes.

    I tried, but it does not work.

    void MySqlModel::addRecord(const QString &str1)
    {
    //add a record
    ......

    //update the model
    QSqlQueryModel::setQuery("SELECT FieldName1 FROM Table");
    int ct = this->rowCount();
    QModelIndex modelIndex = this->index(ct-1,0);
    this->setData(modelIndex, str1, PortName);
    ......
    }

    I did not overload the setData since I have no class member m_data like yours. I only update the instance of MySqlModel.


  • Moderators

    @CoderJeff Well then in case of add, use beginInsertRows and endInsertRows. This too will update the model and thus the view. Can you post the complete code ? Its harder to guess how you are trying to implement it.



  • @p3c0

    C++ code:

    class MySqlModel : public QSqlTableModel
    {
    Q_OBJECT

    public:
    MySqlModel(QObject *parent = 0);
    ~MySqlModel();

    enum Roles {
        Field1 = Qt::UserRole + 1,
        Field2
    };
    
    QHash<int, QByteArray> roleNames() const;
    QVariant data(const QModelIndex &index, int role) const;
    
    Q_INVOKABLE void addRecord(const QString &str1, const QString &str2, const QString &str3);
    

    private:
    static void createConnection();
    static void closeConnection();

    static QSqlDatabase m_db;
    

    };

    QHash<int, QByteArray> MySqlModel::roleNames() const
    {
    QHash<int, QByteArray> roles;
    roles[Field1] = "field1";
    roles[Field2] = "field2";
    return roles;
    }

    QVariant MySqlModel::data(const QModelIndex &index, int role) const
    {
    if (!index.isValid())
    return QVariant();

    QString fieldName;
    switch (role) {
        case Field1: fieldName = QStringLiteral("FieldName1"); break;
        case Field2: fieldName = QStringLiteral("FieldName2"); break;
     }
    if (!this->record().isGenerated(fieldName))
        return QVariant();
    else {
        QModelIndex item = indexInQuery(index);
        if ( !this->query().seek(item.row()) )
            return QVariant();
        return this->query().value(fieldName);
    }
    return QVariant();
    

    }

    void MySqlModel::addRecord(const QString &str1, const QString &str2, const QString &str3)
    {
    QSqlQuery query;
    bool bPrepare = query.prepare("INSERT INTO Portfolio (fieldName1, "
    "fieldName2,"
    "fieldName3)"
    "VALUES (?,?,?)");

    query.addBindValue(str1);
    query.addBindValue(str2);
    query.addBindValue(str3);
    query.exec();
    

    QSqlQueryModel::setQuery("SELECT FieldName1 FROM Portfolio")
    int ct = this->rowCount();

    QModelIndex modelIndex = this->index(ct-1,0);
    this->setData(modelIndex, str1, Field1);
    
    return;
    

    }

    Qml code:
    TableView{
    id: myTableView
    objectName: "myTableViewObj"
    anchors.top: rowRadio.bottom
    anchors.left: rowRadio.left
    anchors.right: rowRadio.right
    anchors.bottom: parent.bottom
    anchors.topMargin: 8
    TableViewColumn{ role: "field1"; title: "FieldName1"}
    headerVisible: false
    model: SQQL
    }



  • I changed my code. It works finally.

    Add a Q_INVOKABLE function:

    Q_INVOKABLE QObject* model()
    {
    QSqlQueryModel::setQuery("SELECT FieldName1 FROM Portfolio")
    return mySqlModelInstance;
    }

    In QML, after calling addRecord or deleteRecord, call the above function and update the TableView's model.

    {
    ......
    myModel.addRecord();
    tableView.model = myModel.model();
    ......
    }

    {
    ......
    myModel.deleteRecord();
    tableView.model = myModel.model();
    ......
    }

    I did not use setData or dataChanged.


  • Moderators

    @CoderJeff But then you are assigning model and again and again when something is added or removed. Instead try to use beginResetModel and beginInsertRows.



  • @p3c0

    I will try it.

    QML almost drives me crazy.

    I am making a desktop program. I find it is very inconvenient to integrate QML and C++ while it is much more convenient using QWidget. I check the difference between QML and QWidget. QML is encapsulated and all its elements are private. It is not easy to access the elements of QML. On the contrary, QWidget on GUI are public. User can access them freely.

    Do you agree with me?


  • Moderators

    @CoderJeff Well new QtQuick Controls are still evolving. Many of the important properties are still under development and hence they are private.

    I find it is very inconvenient to integrate QML and C++ while it is much more convenient using QWidget

    Depends upon the requirement. I have been using components like Item, ListView etc.. with C++ with no problem. Finding Items, connecting to QML signals too work.

    QML is encapsulated and all its elements are private.

    Yes they are (assuming creating from C++). But you can create Items from C++ using QQuickItem. May there are troubles in making them public. Or may be in future they could be made public. Or may be they are not meant to be made.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.