Results 1 to 9 of 9

Thread: qt5 QAbstractListModel C++ model in QML

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jun 2016
    Posts
    99
    Thanks
    18
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default qt5 QAbstractListModel C++ model in QML

    Here is my code for a listView model. It compiles part way and then hangs. I'm not sure if I need to declare msg QList as pointer or not (sidebar: if I do would like to allocate memory on the heap, to do I use the keyword "new"?). It seems to hang on setting the context property in main.cpp when I run the debugger with break points. This all new to me so I'm still sorting out how everything works together in the model to put data in a list to display. My understanding is I create a model in C++ which must include the rowCount(), roleNames(), data() functions. Will also need an enum var to declare the roleNames to be used. Will also need some sort of data structure (struct) to store the data from the database using roleNames. I believe the roleNames have to be the same name as the column header in the sqlite database. I use a sql statement to select data from the database then I loop through the query and add it to the data struct. Use the data struct to populate the list. each row I pull from the database is an element of the list. Is this understanding correct? I think one thing I have trouble with is managing the pointer I'm using... Also little fuzzy on setting the root context (methodology) I know this is a lot but I'm learning and any help is appreciated.

    background:
    I need to create a user event log that monitors the activity of a user in an application (weld programming application).

    I have created a new class to push user events to a database e.g. when they change settings or change weld program. (I have completed this part successfully).


    now I need to display this information in a dialogue for viewing so that managers can monitor employee activity in the application.


    Evidently I need to be able to filter by date and by user once I can get the display working. Also need to figure out how to create a new table every day and delete old tables after 30 days I figure I can create method to create tables and delete tables. Then is just pretty-up the display.
    I have decided to use C++ model to display this and the application GUI is in QML so will need to display in QML I'm using Qt5.5


    Qt Code:
    1. #ifndef SQLITEMODEL_H
    2. #define SQLITEMODEL_H
    3.  
    4. #include <assert.h>
    5. #include <list>
    6. #include <QList>
    7. #include <QColor>
    8. #include <QObject>
    9. #include <QDebug>
    10. #include <QString>
    11. #include <QFileInfo>
    12. #include <QDateTime>
    13. #include <QQmlError>
    14. #include <QQmlApplicationEngine>
    15. #include <QQmlEngine>
    16. #include <QQmlContext>
    17. #include <QtSql/QSqlDatabase>
    18. #include <QtSql/QSqlQuery>
    19. #include <QtSql/QSqlError>
    20. #include <QtSql/QSqlRecord>
    21. #include <QModelIndex>
    22. #include <QAbstractListModel>
    23.  
    24. struct userEventLogMsg{
    25. //hold all values for a single list entry,
    26. QString id;
    27. QString username;
    28. QString eventmessage;
    29. QDateTime datetime;
    30. };
    31.  
    32. class sqliteModel:public QAbstractListModel
    33. {
    34. Q_OBJECT
    35.  
    36. public:
    37. explicit sqliteModel(QObject *parent = 0);
    38.  
    39. ~sqliteModel();
    40.  
    41. enum userEventRoles {idRole, nameRole, msgRole, dateRole = Qt::UserRole + 220};
    42.  
    43. int rowCount(const QModelIndex & parent) const;
    44.  
    45. QHash<int, QByteArray> roleNames() const;
    46.  
    47. QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
    48.  
    49. void addEvent(const userEventLogMsg &msg);
    50.  
    51. private:
    52. QList<userEventLogMsg> m_msgList;
    53. };
    54.  
    55. #endif // SQLITEMODEL_H
    To copy to clipboard, switch view to plain text mode 

    =========================================== CPP FILE =================================================

    Qt Code:
    1. #include "sqlitemodel.h"
    2.  
    3. sqliteModel::sqliteModel(QObject *parent):QAbstractListModel(parent)
    4. {
    5.  
    6. }
    7.  
    8. sqliteModel::~sqliteModel()
    9. {
    10.  
    11. }
    12.  
    13. int sqliteModel::rowCount(const QModelIndex &parent) const
    14. {
    15. Q_UNUSED(parent);
    16. return m_msgList.count();
    17. qDebug()<< m_msgList.count();
    18. }
    19.  
    20. QHash<int, QByteArray> sqliteModel::roleNames() const
    21. {
    22. QHash<int, QByteArray> roleNames;
    23. roleNames.insert(idRole, "id");
    24. roleNames.insert(nameRole, "userName");
    25. roleNames.insert(msgRole, "eventMessage");
    26. roleNames.insert(dateRole, "dateTime");
    27. qDebug()<< roleNames;
    28. return roleNames;
    29. }
    30.  
    31. QVariant sqliteModel::data(const QModelIndex &index, int role) const
    32. {
    33. if (index.row() < 0 || index.row() >= m_msgList.count())
    34. {
    35. return QVariant();
    36. qDebug() << index.row();
    37. }
    38.  
    39. QVariant text;
    40.  
    41. if(role == idRole)
    42. {
    43. userEventLogMsg msg = m_msgList.at(0);
    44. text = msg.id;
    45. qDebug() << text;
    46. }
    47. else if(role == nameRole)
    48. {
    49. userEventLogMsg msg = m_msgList.at(1);;
    50. text = msg.username;
    51. qDebug() << text;
    52. }
    53. else if(role == msgRole)
    54. {
    55. userEventLogMsg msg = m_msgList.at(2);;
    56. text = msg.eventmessage;
    57. qDebug() << text;
    58. }
    59. if(role == dateRole)
    60. {
    61. userEventLogMsg msg = m_msgList.at(3);
    62. text = msg.datetime.toLocalTime().toString("M'/'d'/'yyyy' 'h:mm:ss ap" );
    63. qDebug() << text;
    64. }
    65. qDebug() << text;
    66. return text;
    67. }
    68.  
    69. void sqliteModel::addEvent(const userEventLogMsg &msg)
    70. {
    71. qDebug()<<"made it inside addEvent 1";
    72. beginInsertRows(QModelIndex(), 0, 0);
    73. qDebug()<<"made it inside addEvent 2";
    74. //m_msgList->insert(0, msg);
    75. m_msgList.append(msg);
    76. //qDebug() << m_msgList;
    77.  
    78. endInsertRows();
    79. }
    To copy to clipboard, switch view to plain text mode 
    ============================================ main.cpp ================================================

    Qt Code:
    1. #include <QGuiApplication>
    2. #include <QQmlApplicationEngine>
    3. #include <QSqlDatabase>
    4. #include "sqlitemodel.h"
    5.  
    6. int main(int argc, char *argv[])
    7. {
    8. QGuiApplication app(argc, argv);
    9.  
    10. sqliteModel *model = new sqliteModel;
    11.  
    12. db = QSqlDatabase::addDatabase("QSQLITE");
    13. db.setDatabaseName("/home/amet/git/rnd/userLog.db");
    14. db.open();
    15.  
    16. if(!db.open()){
    17. qDebug() <<"error in opening DB";
    18. }
    19. else{
    20. qDebug() <<"connected to DB" ;
    21. }
    22.  
    23. QSqlQuery myQuery("SELECT id, userName, eventMessage, dateTime FROM userlogevents");
    24.  
    25. if(myQuery.exec("SELECT id, userName, eventMessage, dateTime FROM userlogevents")){
    26. qDebug()<<"sql statement exicuted fine";
    27. }
    28. else{
    29. qDebug() <<"Errors accured with sql statement";
    30. qDebug() <<myQuery.lastError();
    31. }
    32.  
    33. while (myQuery.next()){
    34. userEventLogMsg *msg = new userEventLogMsg;
    35. QString myString = myQuery.value(0).toString();
    36. msg->id.insert(0, myString);
    37. //msg->username.append(myQuery.value(3).toString());
    38. //msg->eventmessage.append(myQuery.value(4).toString());
    39. //msg->datetime.append(myQuery.toLocalTime().toString("M'/'d'/'yyyy' 'h:mm:ss ap'"));
    40. model->addEvent(*msg);
    41. }
    42.  
    43. QQmlApplicationEngine engine;
    44. QQmlContext *contxt = engine.rootContext();
    45. contxt->setContextProperty("sqliteModel", model);
    46. return app.exec();
    47. }
    To copy to clipboard, switch view to plain text mode 

    ============================================ main QML ================================================

    Qt Code:
    1. import QtQuick 2.6
    2. import QtQuick.Layouts 1.1
    3. import QtQuick.Controls 1.3
    4. import QtQuick.Window 2.2
    5. import QtQuick.Dialogs 1.2
    6. import QtQuick.Layouts 1.1
    7. import QtQuick.Controls 2.0
    8. import Qt.labs.folderlistmodel 2.1
    9.  
    10. //---/ Display Rows from database in tableView /---//
    11. Window
    12. {
    13. ListView
    14. {
    15. width: 600; height: 250
    16. //---/ C++ model set using context property in main.cpp /---//
    17. model: sqliteModel
    18. //---// items are drawn by a delegate. /---//
    19. delegate: Text { text: "Record: " + id + ", " + ", " + userName + ", " + eventMessage + ", " + dateTime}
    20. }
    21. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: qt5 QAbstractListModel C++ model in QML

    Quote Originally Posted by jfinn88 View Post
    if I do would like to allocate memory on the heap, to do I use the keyword "new"?).
    Yes.
    But QList will allocate its memory on the heap anyway.

    Quote Originally Posted by jfinn88 View Post
    It seems to hang on setting the context property in main.cpp when I run the debugger with break points.
    strange, that part looks ok.
    You are just not loading any QML.

    Quote Originally Posted by jfinn88 View Post
    My understanding is I create a model in C++ which must include the rowCount(), roleNames(), data() functions.
    Yes, correct.
    rowCount() and data() are pure virtual and need to be implemented in order to the class to be instantiable.
    roleNames() is necessary for the QML integration.

    Quote Originally Posted by jfinn88 View Post
    Will also need an enum var to declare the roleNames to be used.
    You already have the enum for the roles.
    The only thing there is that you need to base the first enum relative to Qt::UserRole, otherwise the first role's integer value will be 0.

    Quote Originally Posted by jfinn88 View Post
    I believe the roleNames have to be the same name as the column header in the sqlite database.
    No, these do not have to relate.
    The role names are purely for the QML engine.

    Quote Originally Posted by jfinn88 View Post
    I use a sql statement to select data from the database then I loop through the query and add it to the data struct.
    You don't need to allocate the instance with new, in fact you are currently leaking each object.

    Quote Originally Posted by jfinn88 View Post
    Use the data struct to populate the list.
    There is currently a mismatch between what you tell beginInsertRows() and usng append().
    Your beginInsertRows() values say that you are adding the new entry at the beginning, while append() then actually adds it to the end of the list.

    Quote Originally Posted by jfinn88 View Post
    each row I pull from the database is an element of the list. Is this understanding correct?
    Yes.

    Your data() method is currently incorrect though.
    The role selects which of the fields of one entry you need to return, that part is correct.

    But which message to get from the list needs to be derived from the index, i.e. the message that is shown in the respective row.

    Alternatively to creatng your own list model you could consider deriving from QSqlQueryModel, adding roleNames() and modifying data() such that is used the role information so that it ask the base class for the right column.

    Cheers,
    _

  3. #3
    Join Date
    Jun 2016
    Posts
    99
    Thanks
    18
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: qt5 QAbstractListModel C++ model in QML

    Okay I fixed my enumuration var

    Qt Code:
    1. enum userEventRoles {idRole= Qt::UserRole + 220, nameRole, msgRole, dateRole};
    To copy to clipboard, switch view to plain text mode 

    I still don't fully understand the Roles concept....

    ================================================== ===========================================

    Quote Originally Posted by anda_skoa View Post
    You don't need to allocate the instance with new, in fact you are currently leaking each object.
    What do you mean leaking objects ? I'm I miss handling something...?

    ================================================== ===========================================

    Quote Originally Posted by anda_skoa View Post
    strange, that part looks ok.
    You are just not loading any QML.
    If setting the contextProperty is fine how do I get the QML to load?

    ================================================== ===========================================

    Quote Originally Posted by anda_skoa View Post
    Your data() method is currently incorrect though.
    The role selects which of the fields of one entry you need to return, that part is correct.
    But which message to get from the list needs to be derived from the index, i.e. the message that is shown in the respective row.
    Can you explain this part that I'm messing up on more?

    ================================================== ===========================================

    Quote Originally Posted by anda_skoa View Post
    There is currently a mismatch between what you tell beginInsertRows() and usng append().
    Your beginInsertRows() values say that you are adding the new entry at the beginning, while append() then actually adds it to the end of the list.
    Switch back to insert function but not sure if it works...

    Qt Code:
    1. void sqliteModel::addEvent(const userEventLogMsg &msg)
    2. {
    3. qDebug()<<"made it inside addEvent 1";
    4. beginInsertRows(QModelIndex(), 0, 0);
    5. qDebug()<<"made it inside addEvent 2";
    6. m_msgList->insert(0, msg);
    7. endInsertRows();
    8. }
    To copy to clipboard, switch view to plain text mode 

    ================================================== ===========================================

    Quote Originally Posted by anda_skoa View Post
    Alternatively to creatng your own list model you could consider deriving from QSqlQueryModel, adding roleNames() and modifying data() such that is used the role information so that it ask the base class for the right column.
    I would like to see if I can get the model to display in QML first however I have tried using QSqlQuerModel (most likely improperly) with no avail. Once I get the sqlite data to display in QML could you help me implement the QSqlQueryModel ? Could you possibly help me set that up properly once I can get the listView to display?

    ================================================== ===================
    update 1:
    change userEventLogMsg to normal object (no pointer)

    update 2:

    when I set break point at:

    Qt Code:
    1. QQmlContext *contxt = engine.rootContext();
    To copy to clipboard, switch view to plain text mode 

    The app hangs here and debugger shows: "QML Debugger: Waiting for connection on port 54009..." -> is this the issue why the qml wont load I cant even get a window to show it doesn't compile that far.
    Last edited by jfinn88; 19th August 2016 at 18:38.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: qt5 QAbstractListModel C++ model in QML

    Quote Originally Posted by jfinn88 View Post
    I still don't fully understand the Roles concept....
    Roles initially were meant to have access to several aspects of a single model cell, e.g. the value itself, font to use, text alignment, etc.

    In the context of QML the role is more used to have multiple data elements per entry.

    Quote Originally Posted by jfinn88 View Post
    What do you mean leaking objects ? I'm I miss handling something...?
    Leaking means that memory is lost.
    You are allocating userEventLogMsg objects on the heap (using new) but not releasing that memory with delete anywhere.

    Quote Originally Posted by jfinn88 View Post
    If setting the contextProperty is fine how do I get the QML to load?
    QQmlApplicationEngine::load().


    Quote Originally Posted by jfinn88 View Post
    Can you explain this part that I'm messing up on more?
    You are using hardcoded numbers to access which message you are getting the data from instead of using the row information of the index argument.

    Quote Originally Posted by jfinn88 View Post
    Switch back to insert function but not sure if it works...
    yes, that is consistent now. changing the arguments to beginInsertRows() would have been the other option.

    Quote Originally Posted by jfinn88 View Post
    I would like to see if I can get the model to display in QML first however
    Sensible approach, I agree.

    Quote Originally Posted by jfinn88 View Post
    I have tried using QSqlQuerModel (most likely improperly) with no avail. Once I get the sqlite data to display in QML could you help me implement the QSqlQueryModel ? Could you possibly help me set that up properly once I can get the listView to display?
    if your custom model starts working you should just keep using it.
    The other approach is mainly interesting when the same model should also be shown in a QtWidget table view.


    Quote Originally Posted by jfinn88 View Post
    The app hangs here and debugger shows: "QML Debugger: Waiting for connection on port 54009..." -> is this the issue why the qml wont load I cant even get a window to show it doesn't compile that far.
    If your program runs then it has already fully compiled.
    And you program doesn't hang, it just doesn't do anything because it doesn't have anything to do since you haven't loaded any UI.

    You currently have a QML engine that has access to the model but nothing is using that model.

    Cheers,
    _

    Quote Originally Posted by jfinn88 View Post
    I still don't fully understand the Roles concept....
    Roles initially were meant to have access to several aspects of a single model cell, e.g. the value itself, font to use, text alignment, etc.

    In the context of QML the role is more used to have multiple data elements per entry.

    Quote Originally Posted by jfinn88 View Post
    What do you mean leaking objects ? I'm I miss handling something...?
    Leaking means that memory is lost.
    You are allocating userEventLogMsg objects on the heap (using new) but not releasing that memory with delete anywhere.

    Quote Originally Posted by jfinn88 View Post
    If setting the contextProperty is fine how do I get the QML to load?
    QQmlApplicationEngine::load().


    Quote Originally Posted by jfinn88 View Post
    Can you explain this part that I'm messing up on more?
    You are using hardcoded numbers to access which message you are getting the data from instead of using the row information of the index argument.

    Quote Originally Posted by jfinn88 View Post
    Switch back to insert function but not sure if it works...
    yes, that is consistent now. changing the arguments to beginInsertRows() would have been the other option.

    Quote Originally Posted by jfinn88 View Post
    I would like to see if I can get the model to display in QML first however
    Sensible approach, I agree.

    Quote Originally Posted by jfinn88 View Post
    I have tried using QSqlQuerModel (most likely improperly) with no avail. Once I get the sqlite data to display in QML could you help me implement the QSqlQueryModel ? Could you possibly help me set that up properly once I can get the listView to display?
    if your custom model starts working you should just keep using it.
    The other approach is mainly interesting when the same model should also be shown in a QtWidget table view.


    Quote Originally Posted by jfinn88 View Post
    The app hangs here and debugger shows: "QML Debugger: Waiting for connection on port 54009..." -> is this the issue why the qml wont load I cant even get a window to show it doesn't compile that far.
    If your program runs then it has already fully compiled.
    And you program doesn't hang, it just doesn't do anything because it doesn't have anything to do since you haven't loaded any UI.

    You currently have a QML engine that has access to the model but nothing is using that model.

    Cheers,
    _

Similar Threads

  1. Replies: 8
    Last Post: 11th March 2016, 10:56
  2. Replies: 1
    Last Post: 29th August 2013, 06:41
  3. ListView model binding to QAbstractListModel
    By michalk in forum Qt Quick
    Replies: 1
    Last Post: 16th July 2011, 10:21
  4. QAbstractListModel
    By Archa4 in forum Newbie
    Replies: 9
    Last Post: 9th February 2011, 15:43
  5. QAbstractListModel searching.
    By ComaWhite in forum Qt Programming
    Replies: 1
    Last Post: 15th June 2009, 19:41

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.