How to remove items from c++ model inside qml?



  • My model is subclass of QAbstractListModel
    What methods should I implement? I guess it's deleteRows but how do I "call" them inside of qml code? Especially if I don't have access to my model (or it's methods because of some strange qt magic which turns my model to QQmlDMAbstractItemModelData which has all the rolls but no methods, and I don't want to store my model as property in all qml delegates)
    I mean then I do something like role = data it implicitly calls setData, when I just use role it uses data(). But how to make it remove current item (at index)?


  • Moderators

    Hi @flashmozzg
    You can create a Q_INVOKABLE function in C++ model which will internally call removeRow. Current item row can be retrieved from the QML itself and then pass to this function.



  • @p3c0 said:

    Hi @flashmozzg
    You can create a Q_INVOKABLE function in C++ model which will internally call removeRow. Current item row can be retrieved from the QML itself and then pass to this function.

    Yeah I ended up doing like that. With propagating remove signal to the top and then deleting it. Was hoping there was some built-in mechanism for doing that like there is for reading and modifying items data.



  • I have the same question. But I did not solve it using removeRow.

    In C++ (Q_INVOKABLE):
    void MyCppClass::remove(const int idx)
    {
    bool bExe = mySqlModel->removeRow(idx);
    }

    In QML:
    TableView {
    id: tableView
    ......
    }

    Button{
    onClicked: myCppClass.remove(tableView.currentRow);
    }

    The application output is:
    QSqlQuery::value: unknown field name ''

    Is there anything wrong with my code?


  • Moderators

    @CoderJeff IsmySqlModel a subclass of QSqlQueryModel ?



  • @p3c0
    No, mySqlModel is a subclass of QSqlTableModel.


  • Moderators

    @CoderJeff Are you sure tableView.currentRow returns a valid row ?



  • @p3c0 said:

    @CoderJeff Are you sure tableView.currentRow returns a valid row ?

    Yes, I debug my code, and the input parameter idx is correct.

    There are six rows. I checked each row's index: 0,1,2,3,4,5.


  • Moderators

    @CoderJeff Ok. Not sure then. I guess there's some other code that is causing the problem.



  • @p3c0

    The entire code is the following.

    In C++ (Q_INVOKABLE):
    void MyCppClass::remove(const int idx)
    {
    bool bExe = mySqlModel->removeRow(idx);
    }

    (Q_INVOKABLE)
    QObject* MyCppClass::selectModel()
    {
    return mySqlModel->selectModel();
    }

    MySqlModel* MySqlModel::selectModel()
    {
    QString strQuery("SELECT field1,field2,field3 FROM table WHERE field1='a'");
    setQuery(QSqlQuery(strQuery));
    return this;
    }

    TableView {
    id: tableView
    TableViewColumn{ role: "field1"; title: "Field1"}
    TableViewColumn{ role: "field2"; title: "Field2"}
    TableViewColumn{ role: "field3"; title: "Field3"}
    model: myCppClass.selectModel()
    ......
    }

    Button{
    onClicked: myCppClass.remove(tableView.currentRow);
    }


  • Moderators

    @CoderJeff Can you check if mySqlModel has data ?
    You can check it by using record.

    mySqlModel->record(idx).value(0).toString() // checking value at column 0 and converting it to string
    


  • @p3c0

    You are right. It is empty.

    I will check the logic.



  • @p3c0

    A weird phenomenon. If using QSqlTableModel, the result is invalid. But if using QSqlQueryModel, the result is the expected.

    Why?

    The following is my debugging code.

    MySqlModel* MySqlModel::selectModel()
    {
    QString strQuery("SELECT field1,field2,field3 FROM table WHERE field1='a'");
    
        QVariant val0;
        QVariant val1;
    
        setQuery(QSqlQuery(strQuery));
        val0 =  record(0).value(0);  //Invalid
        val1 =  record(0).value(1);  //Invalid
    
        QSqlQueryModel model;
        model.setQuery(strQuery);
        val0 =  model.record(0).value(0);  //Correct. The result is the expected.
        val1 =  model.record(0).value(1);  //Correct. The result is the expected.
    
    return this;
    }
    

  • Moderators

    @CoderJeff According to the doc here, setTable should be used instead of setQuery for QSqlTableModel.



  • @p3c0 said:

    @CoderJeff According to the doc here, setTable should be used instead of setQuery for QSqlTableModel.

    The cause is not as simple as I expected. Although I change MySqlModel's parent class to QSqlQueryModel instead of QSqlTableModel, it still does not work. But if using QSqlQueryModel directly, it works.

    I am confusing.

    class MySqlModel : public QSqlQueryModel
    {
    }

    MySqlModel* MySqlModel::selectModel()
    {
    QString strQuery("SELECT field1,field2,field3 FROM table WHERE field1='a'");

    QVariant val0;
    QVariant val1;
    
    //using MySqlQueryModel
    setQuery(strQuery);
    val0 =  record(0).value(0);  //Invalid
    val1 =  record(0).value(1);  //Invalid
    
    //using QSqlQueryModel directly
    QSqlQueryModel model;
    model.setQuery(strQuery);
    val0 =  model.record(0).value(0);  //Correct. The result is the expected.
    val1 =  model.record(0).value(1);  //Correct. The result is the expected.
    

    return this;
    }


  • Moderators

    @CoderJeff Make sure the query is active before using setQuery.

    QSqlQuery sq;
    sq.prepare(strQuery);
    sq.exec();
    qDebug() << sq.isActive();
    setQuery(sq);
    


  • @p3c0 said:

    @CoderJeff Make sure the query is active before using setQuery.

    QSqlQuery sq;
    sq.prepare(strQuery);
    sq.exec();
    qDebug() << sq.isActive();
    setQuery(sq);
    

    I tested it in your way, but It still did not work. The query is definitely active because the return value of isActive() is true.


  • Moderators

    @CoderJeff Ok. Can you post your complete minimal runnable code somewhere in a zip to test ? Need to check whats actually going on.



  • @p3c0 said:

    @CoderJeff Ok. Can you post your complete minimal runnable code somewhere in a zip to test ? Need to check whats actually going on.

    I sent you the link. Please check it.


  • Moderators

    Hi @CoderJeff,
    Your code looks fine. Use method1 or method3 in MySqlModel. The problem is with the view. TableView inside ScrollView seems to break. I just kept TableView in tab01 and everything worked as expected.



  • @p3c0 said:

    Hi @CoderJeff,
    Your code looks fine. Use method1 or method3 in MySqlModel. The problem is with the view. TableView inside ScrollView seems to break. I just kept TableView in tab01 and everything worked as expected.

    What's the meaning of "kept TableView in tab01"? Can you show me the code?


  • Moderators

    @CoderJeff The contents of tab01.qml file. I just TableView in it and deleted the rest. It works.

    //Tab01.qml
    
    import QtQuick 2.0
    import QtQuick.Controls 1.2
    import QtQuick.Layouts 1.1
    
    TableView{
        id: tableView
        anchors.fill: parent
        TableViewColumn{ role: "id"; title: "Id"}
        TableViewColumn{ role: "name"; title: "Name"}
        TableViewColumn{ role: "author"; title: "Author"}
        TableViewColumn{ role: "year"; title: "Year"}
        model:myCppClass.model()
    }
    


  • @p3c0

    I got it.

    Yes, it works. I change ScrollView to Rectangle, and it also seems work well. But I debugged the code, and fount that the value of val0 and val1 in method1 and method3 were still not right.

    Did you check them?


  • Moderators

    @CoderJeff Hmm that's strange. It doesn't work. Tested it with my other project, works there.


  • Moderators

    @CoderJeff If you replace the code in data() with the following, it will work:

    QVariant MySqlModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
    
        switch(role) {
            case Id: {
                return record(index.row()).value(0).toString();
            }
            case Name : {
                return record(index.row()).value(1).toString();
            }
            case Author : {
                return record(index.row()).value(2).toString();
            }
            case Year : {
                return record(index.row()).value(3).toString();
            }
        }
        return QSqlQueryModel::data(index, role);
    }
    
    ...
    
    setQuery(strQuery);
    var0 = record(1).value(0);
    var1 = record(0).value(1);
    qDebug() << var0 << var1;
    

    Trying to find the reason.



  • @p3c0

    That's what I am confused with. In addition, I tested the following statements, and they all works.

    record(0).indexof(...)
    record(0).index(...)
    data(...)

    I do not understand why only record(0).value(...) can not work.

    I tried to delete a chosen row, but it could not.I do not know whether it is the above reason. I have updated my code. Can you please check them? Thank you.



  • @p3c0 said:

    @CoderJeff If you replace the code in data() with the following, it will work:

    QVariant MySqlModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
    
        switch(role) {
            case Id: {
                return record(index.row()).value(0).toString();
            }
            case Name : {
                return record(index.row()).value(1).toString();
            }
            case Author : {
                return record(index.row()).value(2).toString();
            }
            case Year : {
                return record(index.row()).value(3).toString();
            }
        }
        return QSqlQueryModel::data(index, role);
    }
    
    ...
    
    setQuery(strQuery);
    var0 = record(1).value(0);
    var1 = record(0).value(1);
    qDebug() << var0 << var1;
    

    Trying to find the reason.

    Yes, it works. The value of val0 and val1 are both right this time. But I still can not delete a row.


  • Moderators

    @CoderJeff That is because QSqlquerymodel is a read-only model. See here. You will need to delete the row using another query and then update the view.



  • @p3c0

    I tried to use QSqlTableModel, and it also did not work.

    Here I do not want to use another query. I just want to delete a row from the model instead of the table. In other words, only on GUI it seems to be deleted, but it is still in the table actually.


  • Moderators

    @CoderJeff Sorry I too donot know about this. This QTBUG-37538 seems to be similar but using ListView.
    Test it with QTableView to see if it works ?



  • @p3c0

    I used a temporary table to achieve my expected function. In addition, call setTable and select as the SqlTableModelTest did.


  • Moderators

    @CoderJeff Ok, but then why not execute a delete query instead of creating another temporary table ? I guess removeRow for QSqlTableModel too does the same internally.



  • @p3c0

    Is a table not necessary when using a delete query?


  • Moderators

    @CoderJeff table is required ofcourse but why duplicate it first. What you require can be achieved by firing another delete query and then reloading the model so that it reflects in TableView.



  • @p3c0

    Actually, I was wondering what you said for an evening, and still did not understand it. Can you show me an example?

    If not duplicate the table first, after deleting a record from the table, how can I restore it?


  • Moderators

    @CoderJeff Since you only want to delete a row it can be done as follows:

    //MySqlModel.cpp
    void MySqlModel::removeMyRow(int r)
    {
        QString strQuery("DELETE FROM Book where id="+QString::number(r));
        QSqlQuery sq;
        sq.prepare(strQuery);
        sq.exec();
    
        strQuery = "SELECT * FROM Book";
        sq.prepare(strQuery);
        sq.exec();
        setQuery(sq);
    }
    

    This will delete the row first and then reload the model with updated data.



  • @p3c0

    That is not what I mean.

    I just want to delete a row on GUI, not from the table. What you did will definitely delete a row from the table.


  • Moderators

    @CoderJeff Oh. Sorry, I thought its other way. Didn't understood what you meant here.


Log in to reply
 

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