Page 1 of 2 12 LastLast
Results 1 to 20 of 24

Thread: custom QAbstractTableModel class updating QTableView

  1. #1
    Join Date
    May 2016
    Posts
    13

    Default custom QAbstractTableModel class updating QTableView

    Hi all,

    I already read a few threads on this forum dealing with similar issues but didn't provide "the" solution.

    I built my own QAbstractTableModel class which fills a QTableView with data from a .csv file. The file is used for logging purposes and stores its (new) data every 10 minutes. The data is shown inside a QTableView but needs to be updated manually (button or closing and reopening the QDialog). What I want to achieve is that the view is automatically updated whenever new data is written to the .csv file.

    The code snippets of my TableModel looks as follows:
    Qt Code:
    1. int QCsvTableModel::rowCount(const QModelIndex &parent) const
    2. {
    3. Q_UNUSED(parent);
    4. return csvMatrix.rowCount();
    5. }
    6.  
    7. int QCsvTableModel::columnCount(const QModelIndex &parent) const
    8. {
    9. Q_UNUSED(parent);
    10. return csvMatrix.columnCount();
    11. }
    12.  
    13. QVariant QCsvTableModel::data(const QModelIndex &index, int role) const
    14. {
    15. if (index.isValid())
    16. if (role == Qt::DisplayRole || role == Qt::EditRole)
    17. return csvMatrix.at(index.row(), index.column());
    18. return QVariant();
    19. }
    20.  
    21. bool QCsvTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
    22. {
    23. if (index.isValid() && role == Qt::EditRole) {
    24. csvMatrix.setValue(index.row(), index.column(), value.toString());
    25.  
    26. emit dataChanged(index,index); // No difference if dataChanged is emitted or not
    27.  
    28. return true;
    29. }
    30.  
    31. return false;
    32. }
    To copy to clipboard, switch view to plain text mode 

    MainWindow source:
    Qt Code:
    1. MainWindow::MainWindow(QWidget *parent) :
    2. QMainWindow(parent),
    3. ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6.  
    7. QString fileName = ":/value.csv";
    8.  
    9. if (!fileName.isEmpty()) {
    10. QCsvTableModel *model = new QCsvTableModel(this);
    11.  
    12. QString extension = QFileInfo(QFile(fileName)).completeSuffix();
    13.  
    14. if (extension.toLower() == "csv") // known file extension
    15. model->loadFromFile(fileName);
    16.  
    17. ui->tableView->setModel(model);
    18. } // if fileName ..
    19.  
    20. connect(ui->tableView->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onModelsDataChanged(const QModelIndex&, const QModelIndex&)));
    21. //connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onModelsDataChanged(const QModelIndex&, const QModelIndex&))); // Didn't work either ..
    22. }
    23.  
    24. void MainWindow::onModelsDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
    25. {
    26. Q_UNUSED(topLeft);
    27. Q_UNUSED(bottomRight);
    28.  
    29. qDebug() << "The data has changed." << endl;
    30. }
    To copy to clipboard, switch view to plain text mode 

    But the dataChanged() signal isn't fired at all and "The data has changed." never shows up.

    What am I missing? Any help is appreciated!

    Michael

  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: custom QAbstractTableModel class updating QTableView

    The code you posted does not contain any call to setData(), which is where you emit dataChanged().

    Do you edit the table through the view?

    Cheers,
    _

  3. #3
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    Hi anda_skoa,

    thank you for replying!!
    the model gets it's data from the loadFromFile() method which looks as follows:
    Qt Code:
    1. bool QCsvTableModel::loadFromFile(const QString &fileName, const QChar &delim)
    2. {
    3. beginResetModel();
    4.  
    5. csvMatrix.clear();
    6. QChar delimiter;
    7. QFile file(fileName);
    8.  
    9. if (delim == 0) {
    10. QString extension = QFileInfo(file).completeSuffix();
    11. if (extension.toLower() == "csv")
    12. delimiter = QChar(';');
    13. }
    14. else if (delim == QChar('"'))
    15. return false; //the ONLY invalid delimiter is double quote (")
    16. else
    17. delimiter = delim;
    18. if (!file.isOpen())
    19. if (!file.open(QFile::ReadOnly|QFile::Text))
    20. return false;
    21.  
    22. QString temp;
    23. QChar lastCharacter;
    24. QTextStream in(&file);
    25. QList<QString> row;
    26.  
    27. while (true) {
    28. QChar character;
    29. in >> character;
    30. if (in.atEnd()) {
    31. if (lastCharacter == delimiter) //cases where last character is equal to the delimiter
    32. temp = "";
    33. checkString(temp, row, csvMatrix, delimiter, QChar('\n'));
    34. break;
    35. } else if (character == delimiter || character == QChar('\n'))
    36. checkString(temp, row, csvMatrix, delimiter, character);
    37. else {
    38. temp.append(character);
    39. lastCharacter = character;
    40. }
    41. }
    42.  
    43. file.close();
    44. in.flush();
    45. in.reset();
    46.  
    47. return true;
    48.  
    49. endResetModel();
    50. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by MichaH; 5th August 2016 at 11:10.

  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: custom QAbstractTableModel class updating QTableView

    That looks mostly good, but you need to do the endResetModel() before you return

    Regarding your original question: since this method doesn't emit dataChanged() (doesn't need to since it resets the model), why would the slot connected to the signal be called?

    Cheers,
    _

  5. #5
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    My bad! Did that already.

    The code right now looks as follows:
    Qt Code:
    1. bool QCsvTableModel::loadFromFile(const QString &fileName, const QChar &delim)
    2. {
    3. QCsvTableModel::beginResetModel();
    4. csvMatrix.clear();
    5. QChar delimiter;
    6. QFile file(fileName);
    7.  
    8. if (delim == 0) {
    9. QString extension = QFileInfo(file).completeSuffix();
    10. if (extension.toLower() == "csv")
    11. delimiter = QChar(';');
    12. }
    13. else if (delim == QChar('"'))
    14. return false; // The only invalid delimiter is double quote (")
    15. else
    16. delimiter = delim;
    17. if (!file.isOpen())
    18. if (!file.open(QFile::ReadOnly|QFile::Text))
    19. return false;
    20.  
    21. QString temp;
    22. QChar lastCharacter;
    23. QTextStream in(&file);
    24. QList<QString> row;
    25.  
    26. while (true) {
    27. QChar character;
    28. in >> character;
    29. if (in.atEnd()) {
    30. if (lastCharacter == delimiter) // Cases where last character is equal to the delimiter
    31. temp = "";
    32. checkString(temp, row, csvMatrix, delimiter, QChar('\n'));
    33. break;
    34. } else if (character == delimiter || character == QChar('\n'))
    35. checkString(temp, row, csvMatrix, delimiter, character);
    36. else {
    37. temp.append(character);
    38. lastCharacter = character;
    39. }
    40. }
    41.  
    42. file.close();
    43. in.flush();
    44. in.reset();
    45.  
    46. QCsvTableModel::endResetModel();
    47.  
    48. return true;
    49. }
    To copy to clipboard, switch view to plain text mode 

    I just need to update the data structure with replacing the csvMatrix variable since it gets all the model data. But apparently I'm completely lost on that one right now........

  6. #6
    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: custom QAbstractTableModel class updating QTableView

    I am not sure I understand the current state correctly.

    Still something you need help with?

    Cheers,
    _

  7. #7
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    Well yes. The auto-update still doesn't work since I'm not able to figure out what else I'm missing..

  8. #8
    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: custom QAbstractTableModel class updating QTableView

    But the initial loading works?

    Are you calling the same load method for updating or do you use something else?

    Cheers,
    _

  9. #9
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    The data shows up in my QTableView correctly but still when the data of the csv file changes (doesn't matter if that happens outside of the qt app or with a button which adds new data) it doesn't update/refresh itself. It only refreshes if the app is restarted or if the QDialog is closed and reopened.

  10. #10
    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: custom QAbstractTableModel class updating QTableView

    How do you handle these two cases?

    Do you watch the file for changes and then call the load method?

    What do you do in the slot connected to the button? Also reload the file?

    Cheers,
    _

  11. #11
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    When opening the dialog I've connected the dataChanged() signal to a slot which shows a debug message in the console. the connect() call returns true in debug.
    Qt Code:
    1. {
    2. ...
    3. connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onModelsDataChanged(const QModelIndex&, const QModelIndex&)));
    4. ...
    5. }
    6.  
    7. void Dialog::onModelsDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
    8. {
    9. Q_UNUSED(topLeft);
    10. Q_UNUSED(bottomRight);
    11.  
    12. qDebug() << "YES" << endl;
    13.  
    14. // model->loadFromFile(fileName); // fileName = path/to/value.csv
    15. // ui->tableView->setModel(model);
    16. }
    To copy to clipboard, switch view to plain text mode 
    The commented lines (reloading the model) didn't make any difference.

    The button just increments a number when it's clicked and writes it to the file:
    Qt Code:
    1. void Dialog::onButtonPush()
    2. {
    3. QFile log(":/value.csv");
    4.  
    5. QTextStream stream(&log);
    6. x++;
    7.  
    8. if (log.open(QFile::WriteOnly | QFile::Truncate)) {
    9. stream << "Clicked button number;" << x << endl;
    10. ui->pushButton->setText(QString::number(x));
    11. }
    12.  
    13. log.flush();
    14. log.close();
    15. }
    To copy to clipboard, switch view to plain text mode 

    Button and tableview are on the same dialog window and the tableview is supposed to be updated as soon as the data changes - e.g. every time the button is clicked. The updating doesn't work though. :/

  12. #12
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: custom QAbstractTableModel class updating QTableView

    I don't understand this ":/value.csv" filename. That is the name for a file that would ordinarily be compiled into your program as a resource. You can't write to it from a logging process, because it doesn't really exist as a disk file. So if you keep trying to load the same file (from resources), of course the content never changes.

    As anda_skoa has asked, where is your code for watching the log file on disk and reacting if it changes? You should have a QFileSystemWatcher or some other object that is watching this file, and slot connected to its signal that tells your app when the file has changed. Then you load it into your model using the name of the log file on disk, not the name of the file from the resources.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  13. #13
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    Hi d_stranz,

    thanks for your reply.
    I've got a logger class which writes data into that csv file lying on my fs. You're right with the notation - just wanted to shorten it.
    The path to the file is /home/micha/qt-app/value.csv. In my case the delimiter of the csv file is ";" instead of ",".
    The file is loaded correctly into my tableview and as said i just want to update the tableview during runtime which should work with e.g. the dataChanged() signal. New data is written into that file correctly as well (when I open the file from desktop) or when I restart the application the new data is shown correctly in the tableview underneath the "old" data.
    My only problem is that it's not inserted during runtime. The tableview is not showing it until I load the model into it again manually via pushbutton or whatever. I've read a lot about the dataChanged() signal which should be fired as soon as the data (row / column change) of the file changes. Somehow one can connect it to repaint, update, whatever the tableview to automatically show newly added data during runtime. And that is what's not working in my case and I simply don't know why..
    Last edited by MichaH; 5th August 2016 at 18:33.

  14. #14
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: custom QAbstractTableModel class updating QTableView

    The dataChanged() signal applies only to the model, not to your log file. How is your model being notified that the data in the file is changed? What is causing your model to reload the data from the file (besides your manual push button method)?

    I don't see anything in the code you have posted that indicates your program is watching the disk file for changes. There is no automatic connection between the disk file and your model. You have to write some code to watch the disk file that will trigger your model to reload it when it is changed.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  15. #15
    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: custom QAbstractTableModel class updating QTableView

    Quote Originally Posted by MichaH View Post
    When opening the dialog I've connected the dataChanged() signal to a slot which shows a debug message in the console. the connect() call returns true in debug.
    And you are not emitting it in any method that you are executing.

    How is a signal supposed to happen if its emit is not on a code path that is being executed?

    Quote Originally Posted by MichaH View Post
    The commented lines (reloading the model) didn't make any difference.
    Of course that didn't make any difference, there is not a single emit dataChanged() in your load method.
    The load method resets the model, if you want to trigger on that connect to modelReset().

    Quote Originally Posted by MichaH View Post
    The button just increments a number when it's clicked and writes it to the file:
    No call on the model, so how would the model emit a signal?

    Cheers,
    _

  16. #16
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    That's embarrassing... you're absolutely right! a QFileSystemWatcher could do the trick.
    Thank you both so much for your efforts and replies!!! Maybe I'll get back to you if I get stuck on this!

  17. #17
    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: custom QAbstractTableModel class updating QTableView

    Quote Originally Posted by MichaH View Post
    The file is loaded correctly into my tableview and as said i just want to update the tableview during runtime which should work with e.g. the dataChanged() signal.
    The only code you have posted so far that contains an emit for that signal is setData(), but you have not posted any code that calls setData().

    As far as we can tell you just never execute any code that emits dataChanged()

    Quote Originally Posted by MichaH View Post
    My only problem is that it's not inserted during runtime.
    You haven't posted any code that would insert data.

    Quote Originally Posted by MichaH View Post
    I've read a lot about the dataChanged() signal which should be fired as soon as the data (row / column change) of the file changes
    It is emitted then you tell your model to emit it.
    Which you do in setData().
    Which you don't seem to call at all.

    Given your data it is probably never emitted, as that would require that a log record changes.
    But in all likelyhood you are just appending new records to the log file.

    Quote Originally Posted by MichaH View Post
    Somehow one can connect it to repaint, update, whatever the tableview to automatically show newly added data during runtime.
    The table view does that internally.

    Quote Originally Posted by MichaH View Post
    And that is what's not working in my case and I simply don't know why..
    You mean that when you edit a cell in the table view through the UI, then it doesn't call setData()?
    Or that setData() is being called but the signal is not emitted?

    Cheers,
    _

  18. #18
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    You're right, I'm not using setData(..) at all. I'm only calling my own slot loadFromFile(..). I'm finally understanding what's causing my problems. Takes some time to refactor everything but I'm letting you know if I managed to get it working!!
    Thank you for your advices!

  19. #19
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: custom QAbstractTableModel class updating QTableView

    If you are completely replacing the model's content when you re-read the file, and you are not editing the model through the GUI, then don't bother with implementing setData(). In your file load method, first call beginResetModel(), then clear the model and replace the contents, then call endResetModel(). This will take care of notifying any watchers (like your table view) that they need to update their display.

    I use this method when I map the output of a data analysis computation into a QAbstractItemModel. The computational results are almost all entirely new, so it is easier to simply trash the model contents and refill it from the new results. This is slower but far easier than having to try to figure out what's changed and update only those bits.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  20. #20
    Join Date
    May 2016
    Posts
    13

    Default Re: custom QAbstractTableModel class updating QTableView

    Well I thought that I did that in my loadFromFile() slot (as you can see above) since I don't need to edit the model through the gui at all.
    That "static" approach would meet my needs. But I reckon I'm missing the replacing part in my code!?

Similar Threads

  1. QAbstractTableModel, QTableView and restoreState.
    By cydside in forum Qt Programming
    Replies: 0
    Last Post: 10th August 2014, 12:41
  2. Replies: 0
    Last Post: 5th April 2011, 17:51
  3. Custom role in custom QAbstractTableModel
    By hailflex in forum Newbie
    Replies: 3
    Last Post: 10th December 2009, 13:09
  4. Performance with QTableView and QAbstractTableModel
    By MBex in forum Qt Programming
    Replies: 3
    Last Post: 25th February 2009, 09:04
  5. Replies: 3
    Last Post: 29th January 2009, 09:38

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.