Solved QTableWidgetItem, emit signal when cell text changed.
-
I need to emit a signal when the text of a cell is changed through a double click but not when the format of the cell is changed.
The problem is that the
cellChanged
signal is emitted when the font or the background color is changed, what I don't want.Do I need to subclass QTableWidget and create a custom signal?
-
Create an enum, a signal and a slot:
private: enum {CompareRole= Qt::UserRole + 1025}; signals: void cellTextChanged(const QModelIndex& index); private slots: void cellChanged(const QModelIndex& index){ if(index.data(Qt::EditRole) == index.data(CompareRole)) return; tableWidget->model()->setData(index,index.data(Qt::EditRole),CompareRole); emit cellTextChanged(index); }
now
connect(tableWidget->model(),SIGNAL(dataChanged(QModelIndex)),this,SLOT(cellChanged(QModelIndex)));
you can either subclass QTableWidgetItem or implement this directly in the user object
One would expect to be able to use the last argument of QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) but the default Qt models appear to send just the default empty vector so you have to do so some manual work
-
@VRonin, thanks for your answer.
So, I need to subclass QTableWidget. Isn't it? -
@VRonin said in QTableWidgetItem, emit signal when cell text changed.:
you can either subclass QTableWidgetItem or implement this directly in the user object
-
This post is deleted! -
I'm implementing your solution and trying to make it work.
I have a little question: what's is the enum for and what's the use ofCompareRole= Qt::UserRole + 1025
? -
@Patou355 said in QTableWidgetItem, emit signal when cell text changed.:
what's is the enum for
Just to declare a compile time constant
what's the use of
CompareRole= Qt::UserRole + 1025
It's just a random number greater than
Qt::UserRole
The logic here is to store the data in both
Qt::EditRole
and another role (CompareRole
) whendataChanged
is emitted you check those two roles. if they are equal it means that something else changed (font, background color, etc.) if they are different you are sure that theQt::EditRole
is the one that changed -
I think I understand where's my mistake. I need to use a QTableView + QAbstractItemModel couple instead of a standalone QTableWidget, and your code must be implemented in the model and not in the table...
-
@Patou355 said in QTableWidgetItem, emit signal when cell text changed.:
I need to use a QTableView + QAbstractItemModel couple
Not necessarily.
for example:
#include <QWidget> #include <QTableWidget> #include <QVBoxLayout> #include <QPushButton> #include <QLabel> class TestWidget : public QWidget { Q_OBJECT Q_DISABLE_COPY(TestWidget) public: explicit TestWidget(QWidget *parent = Q_NULLPTR) :QWidget(parent) ,m_colorIndex(0) { m_tableWidget=new QTableWidget(this); QVBoxLayout* mainLay=new QVBoxLayout(this); QPushButton* changeColorButton=new QPushButton("Change Color",this); m_lastChangedLabel=new QLabel(this); mainLay->addWidget(m_tableWidget); mainLay->addWidget(changeColorButton); mainLay->addWidget(m_lastChangedLabel); connect(changeColorButton,&QPushButton::clicked,this,&TestWidget::changeColor); connect(m_tableWidget->model(),&QAbstractItemModel::dataChanged,this,&TestWidget::cellChanged); connect(this,&TestWidget::cellTextChanged,this,&TestWidget::setLastChangedLabel); addExampleData(); m_lastChangedLabel->clear(); } private: enum {CompareRole= Qt::UserRole + 1025}; signals: void cellTextChanged(const QModelIndex& index); private slots: void cellChanged(const QModelIndex& index){ if(index.data(Qt::EditRole) == index.data(CompareRole)) return; m_tableWidget->model()->setData(index,index.data(Qt::EditRole),CompareRole); emit cellTextChanged(index); } void changeColor(){ const QColor colors[] = {Qt::blue,Qt::red, Qt::red, Qt::yellow, Qt::green}; for(int i=0;i<15;++i){ for(int j=0;j<5;++j){ m_tableWidget->model()->setData(m_tableWidget->model()->index(i,j),QBrush(colors[m_colorIndex]),Qt::BackgroundRole); } } if(++m_colorIndex>=std::extent<decltype(colors)>::value) m_colorIndex=0; } void setLastChangedLabel(const QModelIndex& index){ m_lastChangedLabel->setText(QStringLiteral("Last changed %1,%2").arg(index.row()).arg(index.column())); } private: QTableWidget *m_tableWidget; int m_colorIndex; QLabel* m_lastChangedLabel; void addExampleData(){ m_tableWidget->setRowCount(15); m_tableWidget->setColumnCount(5); for(int i=0;i<15;++i){ for(int j=0;j<5;++j){ m_tableWidget->model()->setData(m_tableWidget->model()->index(i,j),QStringLiteral("%1,%2").arg(i).arg(j)); } } } };
You can change the background color with the button but the label will show the result only if you modify the text of a cell
-
Hello,
I managed to do it using the last argument inQAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
to fulfill the task. Just wanted to update this in case someone else needs it as it requires less manual work.
connect(tableWidget->model(), &QAbstractItemModel::dataChanged, [this](const QModelIndex& index, const QModelIndex&, const QVector<int>& roles){ if (std::find(roles.begin(), roles.end(), Qt::EditRole) == roles.end())//wasn't caused by entering cell return; //rest of code