Solved Show live data into a table
-
@makopo
? Why/when do you expect it to be called? You have to call it if you wish to insert some rows. (And again fordeleteRows()
.) -
insertRows(int row, int count, const QModelIndex &parent = QModelIndex())
is virtual. I thought it is called automatically?As I understand now
m_stringList.insert(position, "");
only reservs an empty row and I have to add data to the table outside the model class? -
@makopo
Thevirtual
just means that if anything callsQAbstractTableModel::insertRows()
--- even if it knows nothing about your derived class --- the implementation code you have written will be called.The outside world will call
insertRows()
when it wants/needs to. The outside world will do that with no knowledge that you have implemented it viam_stringList.insert()
. The outside world will callsetData()
for the desired column values on newly inserted row(s) after it has calledinsertRows()
. -
@JonB
That was not clear for me. I did not call theheaderData(...)
anddata(...)
function. I only generate a instance of the model class, set the model to the view and get a table. Thought thatsetData(...)
andinsertRows(...)
works on the same way.So as I understand from your last post, I have to do something like this:
//QMainClass.cpp m_tableDataModel->insertRows(0, 1, QModelIndex()); m_tableDataModel->setData(QModelIndex(), QParameterList(), QValueList(), 2); //valuelist is a vector and stores data that should be overwritten in the view m_tableView->setModel(m_tableDataModel);
I'am sry. As a beginner the principle of table view is hard to understand.
-
@makopo
Those are the right calls. But you'll have to work a bit on all your parameters tosetData()
. If you have multiple columns (I don't know if you do) you'll have to callsetData()
for each one. -
A QTableView is just a widget showing your model as a table. Nothing more.
From the looks of it, you did not understand that the model is a wrapper on top of your data structure. From what you wrote it's a QStringList. So you have a model with a single column and as many rows as your string list.
The setData method shall be called to modify the data of one element of your data structure.
If you want to initialise your model with a ready made list, add a method for that.
Note that if your data structure is a QStringList you might as well use the QStringListModel class.
-
I think I understand the principle of a model. Because of this I didn't understand why in this example an empty string is inserted. I think this little for loop is my problem. Why they insert data here?
bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent) { beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { stringList.insert(position, ""); //empty strings are inserted in every row (inserting data alike)? } endInsertRows(); return true; }
In my project I use two columns. Column 0 includes labels and is fixed, so I think I didn't need
SetData()
for the first column. Column 1 gets values of different types and this values should be constantly updated. For both columns I use aQVector<QString>
. To add data to the vector I wrote two functions:QParameterList()
andQValueList()
. I also implement a functionAddData()
that has aQVector<QString>
type as parameter.void DataTableModel::AddData(const QVector<QString>& parameter) { m_parameter = parameter; }
In my
QMainClass
I callAddData()
and with this I generate the first column (column with the fixed values). Now I call the reimplementedSetData()
function and insert (only for test purposes) data to one cell. I got an empty cell. I can fill all cells in column 1 manually, I get an empty column. I think this happens because the empty string overrides the value, I inserted withSetData(...)
?QVariant DataTableModel::SetData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::DisplayRole && index.row() == 0 && index.column() == 1) { return QString(value.toString()); } return QVariant(); } // QMainClass.cpp QVariant value = QString("Test"); m_tableDataModel->InsertRows(QModelIndex(), 0, 3); m_tableDataModel->AddData(QParameterList()); m_tableDataModel->SetData(QModelIndex(), value, 2); m_tableView->setModel(m_tableDataModel);
-
When you add rows, your data structure should contain something that can be returned to the view. Still in the case of your string list, you shall adjust the size of the list and then put some meaningful default data in the new empty spots. In this case empty strings.
Your SetData method does not make sense. By the way, the correct name is setData. Casing is important.
-
Thank you. I will do a next try...:
//DataTableModel.cpp bool DataTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() && role != Qt::EditRole) { return false; } //change only values in column 1 if (index.column() == 1) { m_value.replace(index.row(), value.toString()); emit dataChanged(index, index, { role }); return true; } } bool DataTableModel::insertRows(int row, int count, const QModelIndex& parent) { beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) { m_value.insert(row, ""); } endInsertRows(); return true; } //QMainClass.cpp - called when button is clicked. void QMainClass:QAddTableContent() { QVariant values(QValueList()); //convert QList to QVariant m_tableDataModel->insertRows(0, 5, QModelIndex()); //insert 5 rows m_tableDataModel->AddData(QParameterList()); m_tableDataModel->setData(QModelIndex(), values, 2); }
With this I get a table with 5 rows and 2 columns. The left column shows a list of parameters. The right column is empty.
In the model I set theQt::ItemIsEditable flag
. With this I can edit the right column. I can write into the cells and the new values are stored temporary. Because of this I think thesetData()
function should work correct. Unfortunatly, when the program runs, the right column remains empty (because of the inserted empty string in insertRows?). Are the data inserted on a wrong way? -
How is your data method implemented currently ?
-
This is my data method:
QVariant DataTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= m_parameter.size() && index.row() >= m_value.size()) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case 0: return m_parameter.at(index.row()); break; case 1: return m_value.at(index.row()); break; default: Q_ASSERT(false); } } return QVariant(); }
-
@makopo
One logic fault I see is: In yourinsertRows()
you increase the length ofm_value
(inserting""
into it in the new row), but you do nothing to increase the length ofm_parameter
correspondingly. That should mean that yourdata()
'sreturn m_parameter.at(index.row());
can go wrong/crash after an insertion.Another issue is that as you show your latest
setData()
it has no final, unconditionalreturn
statement at the end. This should have generated a compiler error or warning? -
Correcting this does not bring a change.
When I debug the
setData()
method I with the index and the values for r, c, i and m (does it mean,row
,column
,internal pointer
andmodel
ofQmodelIndex
class?) are undefined. The detailed message for r, c, i, m is "Can not read memory". -
@makopo said in Show live data into a table:
When I debug the setData() method I with the index and the values for r, c, i and m (does it mean, row, column, internal pointer and model of QmodelIndex class?) are undefined.
What are r, c, i and m ?
You should post the complete code of your model class.
By the way, you should use QAbstractModelTester to validate your implementation.
-
@SGaist said in Show live data into a table:
What are r, c, i and m ?
Can not recognize this error, it did not happen again...
I rewrote my model class. The values are shown in the view, but the view did not update automatically. This is the whole code:
#pragma once #include <QAbstractTableModel> #include <QDebug> struct DataTableItem { QString m_parameter; QString m_value; }; class DataTableModel : public QAbstractTableModel { Q_OBJECT public: DataTableModel(QObject* parent = nullptr); DataTableModel(const QList<DataTableItem>& tableItem, QObject* parent = nullptr); //functions of QAbstractItemModel Qt::ItemFlags flags(const QModelIndex& index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role) const override; bool insertRows(int rows, int count, const QModelIndex& parent) override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; const QList<DataTableItem>& GetTableItem() const; private: QList<DataTableItem> m_tableItem; };
#include "DataTableModel.h" //Ctor DataTableModel::DataTableModel(QObject* parent) : QAbstractTableModel(parent) {} DataTableModel::DataTableModel(const QList<DataTableItem>& tableItem, QObject* parent) : QAbstractTableModel(parent), m_tableItem(tableItem) {} const QList<DataTableItem>& DataTableModel::GetTableItem() const { return m_tableItem; } /** * Returns the item flags for the given index. Sets the cells to editable */ Qt::ItemFlags DataTableModel::flags(const QModelIndex& index) const { if (index.isValid()) return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; return QAbstractItemModel::flags(index); } /** * Defines the header label of the table */ QVariant DataTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return QString("Parameter"); break; case 1: return QString("Wert"); break; default: break; } } return QVariant(); } /** * Counts the number of rows which depends from the QStringList that is defined in QVideoMeter.cpp */ int DataTableModel::rowCount(const QModelIndex& parent) const { return m_tableItem.count(); } /** * Defines the number of columns. This table only have two columns */ int DataTableModel::columnCount(const QModelIndex& parent) const { return 2; } bool DataTableModel::insertRows(int row, int count, const QModelIndex& parent) { //the following two methods emits signals that tells the view that the data should be changed beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_tableItem.insert(row, { QString(), QString() }); endInsertRows(); return true; } /** * Defines the data that should be rendered to a table cell * The QModelIndex class is used to locate data in a data model. */ QVariant DataTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() <0 || index.row() >= m_tableItem.count()) { return QVariant(); } const DataTableItem& item = m_tableItem.at(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case 0: return item.m_parameter; break; case 1: return item.m_value; break; default: break; } } return QVariant(); } /* * Sets the role data for the item at index to value. */ bool DataTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (index.isValid() && role == Qt::EditRole) { const int row = index.row(); DataTableItem item = m_tableItem.value(row); switch (index.column()) { case 0: item.m_parameter = value.toString(); break; case 1: item.m_value = value.toString(); break; default: return false; } m_tableItem.replace(row, item); emit dataChanged(index, index, { Qt::DisplayRole, Qt::EditRole }); return true; } return false; }
/** * Called when start button is clicked. Call functions of QAbstractItemModel */ void QVideoMeter::QAddTableContent() { BSTR displayModeName; //COM data type QString parameters = "Framerate"; QString values = QVariant(m_displayMode->GetWidth()).toString(); m_tableDataModel->insertRows(0, 3, QModelIndex()); QModelIndex index = m_tableDataModel->index(0, 0, QModelIndex()); m_tableDataModel->setData(index, parameters, Qt::EditRole); index = m_tableDataModel->index(0, 1 , QModelIndex()); m_tableDataModel->setData(index, values, Qt::EditRole); }
It will be very friendly if someone of you can give my feedback.
-
@makopo
Glancing through your code looks reasonable to me. So what exactly does not work when? You say "but the view did not update automatically.". What does not happen when you do what? I assume you have checked your view is connected to this data model? Have you stepped through in debugger? Put inqDebug()
s, like to ensure youremit dataChanged()
is being hit? -
@JonB said in Show live data into a table:
What does not happen when you do what?
QString values = QVariant(m_displayMode->GetWidth()).toString();
values
get as return value the width of a video frame. The program starts with an initial value. After changing the video format and with this the width of the frame , the actual value is not shown in the table view. Instead of the new value, the old value is shown. I do the same call withqDebug()
and can see that the new frame width is detected on the console.
This is also my requirement to the table view and the model: I have some function calls which detect changes on the video format and these changes should shown in the table view.@JonB said in Show live data into a table:
I assume you have checked your view is connected to this data model?
Have I do that manually? I thought that
QAbstractTableModel
do the job for me.
I callm_tableView->setModel(m_tableDataModel);
. Don't know if this what you mean.@JonB said in Show live data into a table:
Put in qDebug()s, like to ensure your emit dataChanged() is being hit?
How can I test if
dataChanged
is called? What is the corresponding slot?
I also debug the code and found nothing. -
@makopo said in Show live data into a table:
the actual value is not shown in the table view. Instead of the new value, the old value is shown.
I don't know for sure, maybe you need a fixed width or inform the view of the change or
QTreeView::resizeColumnToContents(int column)
or something.Have I do that manually? I thought that QAbstractTableModel do the job for me.
I call
m_tableView->setModel(m_tableDataModel);
. Don't know if this what you mean.Yes, that statement is what attaches the view to the model, it wasn't shown in your code so I didn't know if you had done it.
How can I test if
dataChanged
is called? What is the corresponding slot?I also debug the code and found nothing.
What "corresponding slot"? Put a slot on it if you want to check, or just a
qDebug()
where youremit dataChanged(index, index, { role });
line is.There are also
QAbstractItemModel::layoutAboutToBeChanged()
&QAbstractItemModel::layoutChanged()
signals, I don't know if your situation might need them to tell tell the view about whatever it is that is changing. -
To behave properly these two should look like:
int DataTableModel::rowCount(const QModelIndex& parent) const { return !parent.isValid() ? m_tableItem.count() : 0; } int DataTableModel::columnCount(const QModelIndex& parent) const { return !parent.isValid() ? 2 : 0; }
Make sure
QVideoMeter::QAddTableContent()
is called. And also what exactly ism_displayMode
and doesQVariant
know how to convert it to a string? Better useQString::number
if that is some integer or something.m_tableDataModel->insertRows(0, 3, QModelIndex());
Inserting 3 rows, but setting data for one? Are you sure?
PS:
Qt::ItemFlags DataTableModel::flags(const QModelIndex& index) const { Q_ASSERT(index.isValid()); //< No calling of flags() with invalid indices! return QAbstractItemModel::flags(index) | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; }
-
Thank you. Actually it didn't work. I try your advices, I'am searching for setting up the index, searching for the correct setup of the Widget
parent
, change the type for the second parametervalue
fromQString
toQVariant
toint
, also check ifdataChanged()
emits a signal (yes it does) - all that brings no change. The pasted data is shown in the view, but the parametervalue
doesn't update. And there is the fact that I can edit every cell of the table. And because the edited value are stored temporary I think that there is no problem with the index.
I print the values that are changing to the console, and here I can see that the ouput react to the changes. Still confusing.
Let me give you another example how I get the values (maybe there is an error):
I have a method that reads the color values of the incoming video. With this values I do a calculation and after that I send the result with asignal
to theQMainClass
.redSum = CalculateAverageValue(redValues); greenSum = CalculateAverageValue(greenValues); blueSum = CalculateAverageValue(blueValues); emit SendRGBAverage(redSum, greenSum, blueSum);
In the
QMainClass
I have aslot
that stores the sended data into member variables.:void QVideoMeter::ReceiveRGBAverage(int redSum, int greenSum, int blueSum) { m_redSum = redSum; m_greenSum = greenSum; m_blueSum = blueSum; qDebug() << m_redSum << m_greenSum << m_blueSum; }
The member variables I paste into the
QAddTableItems()
method, that is called insideQAddTableContent()
void QVideoMeter::QAddTableContent() { QAddTableItems("Red", m_redSum ); } void QVideoMeter::QAddTableItems(const QString& parameter, int& value) { if (!m_tableDataModel->GetTableItem().contains({ parameter, value })) { m_tableDataModel->insertRows(0, 1, QModelIndex()); m_tableDataModel->setData(m_tableDataModel->index(0, 0, QModelIndex()), parameter, Qt::EditRole); m_tableDataModel->setData(m_tableDataModel->index(0, 1, QModelIndex()), value, Qt::EditRole); } else { QStopVideo(); } }
But with this I get only the first transfered value, as I wrote above there is no update. On console I can see every change.
When I debug the dataChanged() signal I can see the following:
I'am wondering about the values of the index and also about the value ofm_value
, because 0 is not the value the program start with. Is there an error?
This is my first big programming project. Please be kind.