How to ensure qtablewidget gets updated before another signal
-
I have a dialog box that has 2 widgets, one is tablewidget and the other is a button (The button 'Add' in the picture).
When an item in the tablewidget is in edit (the item has input focus), the user may click the button directly.
So 2 signals will be triggered: tableWidget_cellChanged and button_clicked.In the slot function to button_clicked, the updated cell in tablewidget is processed, but
the button_clicked signal of clicking the button 'Add' always arrives before the tableWidget_cellChanged,
so the slot function to button_clicked always gets an invalid item text.How to ensure the tableWidget_cellChanged is called before the button_clicked?
-
Thanks for responding.
The picture from my side is shown properly, so I thought everyone can see it.
I can't find other places to upload the image to link from here.
My question is simple and general enough yet the answer is a bit challenging to me, because I've searched it with several engines but couldn't find any clue.
So I suppose I might be able to describe it clearly with plain text.Scenario:
Suppose there are 2 widgets in the Qt designer form (a dialog box): a tablewidget and a button.
When editing a cell in the tablewidget (the cell is still in edit mode with the mouse focus), user can click elsewhere or press 'enter' to have the signal 'cellChanged' emitted, updating the cell content.
But the issue arises when the user clicks the button directly as 'elsewhere' when the cell is still in edit mode, then two signals, 'cellChanged' of that tableWidget cell and 'clicked' of that pushButton, will be emitted in the same time with that single click.Expectation:
I need the signal 'cellChanged' gets processed before the signal 'clicked', because in the slot function for that 'clicked' signal, I assume the cell content has been updated and run checks on that content.
But in fact, the 'clicked' signal is processed first, while the cell content has yet been updated, so the checks always failed.Question:
So how can I ensure the cell gets updated prior to the 'clicked' signal ? -
Hi
There is no way to specify the order of signals.
your issue is that user dont finish editing first and hence the signal is not sent.To work around this, you can override
focusOutEvent(QFocusEvent* event) for the tableThis event should come before the button click - as table editor
loses focus when user click the button.Alternatively, disable button untill user finished editing.
-
Also, cellChanged is emitted after the data has changed.
How is your dialog supposed to be working ?
-
Also, cellChanged is emitted after the data has changed.
How is your dialog supposed to be working ?
@SGaist Exactly, my dialog does not work.
I have to shamely tell the users that they have to 'enter' or click elsewhere to change the data and then click the button. -
That's why we would need to take a look at your dialog code as well as how you are using that dialog.
-
Hi
There is no way to specify the order of signals.
your issue is that user dont finish editing first and hence the signal is not sent.To work around this, you can override
focusOutEvent(QFocusEvent* event) for the tableThis event should come before the button click - as table editor
loses focus when user click the button.Alternatively, disable button untill user finished editing.
@mrjj Thanks for proposing the workaround. It looks like the only solution.
The button is only an example and there might be some other widgets (more buttons, clickable links, etc), and even I disable them all for a while, it is still risky to add more widgets and forget to disable them while the tableWidget is under edit.
I'll try your workaround and get back to update with my result.
This is a pretty general question for years, so I'm surprised I couldn't find any clue by googling.
Haven't the Qt original dialog and widgets like tableWidget been revised to support such common user behavior? -
That's why we would need to take a look at your dialog code as well as how you are using that dialog.
@SGaist I'm using Qt4.8, Qt Creator 3.0.1
-
Add Qt Designer Form class with the name QtForm
-
In the form ui edit, add a tableWidget and a pushbutton onto that Form
-
In the constructor of form class
ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); // only accept one row selection
ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // selection on row basis
ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "cellData");
ui->tableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch);4. In the form ui editor, select the tableWidget, 'Go to slot...' -> cellChanged(int,int) to add the slot to this signal, and then add debug code
qDebug() << "entered cellChanged"
5. In the form ui edit, select the pushButton, add slot to the signal 'clicked()' with the debug code
qDebug() << "entered buttonClicked"
From the trace info for the problem scenario, you can see the clicked() signal arrives first.
-
-
Are you calling exec on that dialog ?
On a side note, unless you're locked to 4.8, you should move at least to Qt 5.6 which is the LTS or the more recent version. Qt 4 as seen its last release with 4.8.7 and won't get any new patch unless it's a critical security issue.
-
Are you calling exec on that dialog ?
On a side note, unless you're locked to 4.8, you should move at least to Qt 5.6 which is the LTS or the more recent version. Qt 4 as seen its last release with 4.8.7 and won't get any new patch unless it's a critical security issue.
@SGaist
Thanks for advice.
Yes, I used 'exec' to create this model dialog.
I have to use 4.8 because much more code was developed using Qt4.8 convention and I'd like to save the troubles of porting the code to Qt5 and upwards, though it might be smooth.I understand Qt4.8 is planned to be a stable version, so I'll adopt the workaround and see how it goes.
Thanks again.
-
Qt 4.8 is not a stable version, it has reached end of life. The current "stable" as in "long term support" is Qt 5.6
And unless you're unlucky, the port from Qt 4 to Qt 5 is pretty painless, the old signals/slots syntax works exactly the same as before.
Back to your problem, what do you do exactly with that slot connected to the
clicked
signal ? -
- In the form ui editor, select the tableWidget, 'Go to slot...' -> cellChanged(int,int) to add the slot to this signal, and then add debug code
- In the form ui edit, select the pushButton, add slot to the signal 'clicked()' with the debug code
check the generated moc_[className].h and see if the
connect
for cellChanged comes before or after the one for clicked. (it should come before).To have a quick and dirty fix add Qt::QueuedConnection as argument to the connect of the clicked signal
-
Qt 4.8 is not a stable version, it has reached end of life. The current "stable" as in "long term support" is Qt 5.6
And unless you're unlucky, the port from Qt 4 to Qt 5 is pretty painless, the old signals/slots syntax works exactly the same as before.
Back to your problem, what do you do exactly with that slot connected to the
clicked
signal ?@SGaist
what I did in the slot to the 'clicked' signal is to use the cell data, which is a variable (say., tableValue) updated in the slot function to 'cellChanged' .
Because the variable has yet been updated, the value of tableValue is not correct (it is still the value before update).A very simple usage is
send(tableValue)
in the 'clicked' slot, to send the value out. Because tableValue has not been updated, the value sent out is incorrect.
-
- In the form ui editor, select the tableWidget, 'Go to slot...' -> cellChanged(int,int) to add the slot to this signal, and then add debug code
- In the form ui edit, select the pushButton, add slot to the signal 'clicked()' with the debug code
check the generated moc_[className].h and see if the
connect
for cellChanged comes before or after the one for clicked. (it should come before).To have a quick and dirty fix add Qt::QueuedConnection as argument to the connect of the clicked signal
@VRonin
Thanks for help.
I don't see moc_<class>.h, but moc_<class>.cpp instead.In this file, the slots are not 'connect'. They're dispatched by the id, something like
void QtForm::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { Q_ASSERT(staticMetaObject.cast(_o)); QtForm*_t = static_cast<QtForm*>(_o); switch (_id) { case 0: _t->on_tableWidget_cellChanged((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; case 1: _t->on_pushButton_clicked(); break; default: ; } } }
So there is no way to add the quick-and-dirty fix with Qt::QueuedConnection
-
@VRonin
Thanks for the clarification.
I couldn't find any connect call in the ui_<class>.h, either.
Something looks like the function required:QMetaObject::connectSlotsByName(QtFrame);
This is in the setupUi.
-
@VRonin
Thanks for the clarification.
I couldn't find any connect call in the ui_<class>.h, either.
Something looks like the function required:QMetaObject::connectSlotsByName(QtFrame);
This is in the setupUi.
@qt_keen_learner
Hi , If you use Creator to create/hook up signal and slot then no
connect is generated.
Its based on auto connect feature where
it looks for slots that matcheson_ObjectName_SignalName
So there is no connects in setupUI() :)
Update:
To do it yourself, simply rename slot to not follow syntax and
you can hook it up after setupUI and append t::QueuedConnection as wanted. -
Can you describe the workflow of that dialog ?
-
@qt_keen_learner
Hi , If you use Creator to create/hook up signal and slot then no
connect is generated.
Its based on auto connect feature where
it looks for slots that matcheson_ObjectName_SignalName
So there is no connects in setupUI() :)
Update:
To do it yourself, simply rename slot to not follow syntax and
you can hook it up after setupUI and append t::QueuedConnection as wanted.@mrjj
Thanks for the update.
After learning QueuedConnection, I found it is related with multi-thread programing, but my case is single-thread, so I slightly suspect if this can work.I also tried another workaround:
I implemented the slot to 'itemClicked', using a flag to call on_tableWidget_cellChanged. i.e.,
when the slot on_tableWidgetItem_cellClicked is called, a bool variable 'toUpdate' is set to true;
so then when the slot to button_clicked is called:As shown in the code below, the problem is, if I explicitly call on_tableWidget_cellChanged(prevRow, prevCol), the value is the old value rather than the text I edited and about to take effect!
So, this workaround still does not work. There is no way for me to get the new text before the cellChanged signal is sent by the Qt system :-(void QtForm::on_pushButton_clicked() { if (toUpdate) this->on_tableWidget_cellChanged(m_nPrevRow, m_nPrevCol); send(cellData); } void QtForm::on_tableWidget_cellClicked(int row, int col) { m_nPrevRow = row, m_nPrevCol = col; toUpdate = true; } void QtForm::on_tableWidget_cellChanged(int row, int col) { // update cellData toUpdate = false; }