Qt force QCheckBox to emit signal on setChecked - qt

If I called QCheckBox::setChecked( x ) the toggled signal is only emitted if x is not the same as the current checkbox state. I understand the logic behind this, to avoid signaling if nothing has changed. However, in some situations where I have a more complicated widgets setup, I need the signal to always be emitted. This ensures anybody who has connected to the checkbox will receive the first state.
Is there a way to have QCheckBox::setChecked(bool) emit a signal regardless of whether the state has changed?
My simple workaround now is to just force the checkbox into multiple states by doing setChecked(!x) and setChecked(x). I was hoping for a more correct way of doing this.

Looking into the QAbstractButton implementation, I found the following lines of code:
if (!d->checkable || d->checked == checked) {
if (!d->blockRefresh)
checkStateSet();
return;
}
where checkStateSet is a virtual function. QCheckBox overrides this and emits a stateChanged() signal only if the state changed.
I haven't tested this, but I think d->blockRefresh is set to false if you call QCheckBox::setChecked( ... ) directly.
If this is the case, it means you could subclass QCheckBox and override the checkStateSet() method to something like this:
void MyCheckBox::checkStateSet()
{
QCheckBox::checkStateSet();
if( m_oldState == checkState() )
{
// emit the signal here, as QCheckBox::checkStateSet() didn't do it.
emit stateChanged( m_oldState );
}
else
{
// don't emit the signal as it has been emitted already,
// but save the old state
m_oldState = checkState();
}
}
where the header file contains
private:
Qt::CheckState m_oldState;
which must be initialised to Qt::Unchecked in the constructor.

Here is another solution which may or may not be possible for your case:
If you can be 100% sure that your signals and slots are connected before the checkbox has a chance to change its state, every connected class can initialize itself safely assuming the checkbox is not checked. This is because checkboxes are always unchecked upon construction.
This way you might not have to call setChecked() after connecting the signals.
However, This approach does not work if there is a chance a signal gets connected after the checkbox has already changed. I'm not 100% fond of this approach myself but it might be an option for you nevertheless.

One way would be to subclass QCheckBox and implement the emitting of signals in that where you need it, for example :
class MyCheckBox : public QCheckBox
{
Q_OBJECT
public:
MyCheckBox(QWidget *parent = 0) : QCheckBox(parent) {};
virtual void setChecked(bool checked) {
QCheckBox::setChecked(checked); emit checkWasSet(checked);
};
signals:
void checkWasSet(bool value);
};
Now use this class instead of the regular QCheckBox class, and you can connect to the checkWasSet() signal for whenever the check state is set.

You could emit the signal with the current state yourself:
checkbox.stateChanged.emit(checkbox.checkState())

Related

How to fire a signal with QMetaObject::activate

I found an interesting article on how to impement QObject with dynamic properties (see C++ class DynamicObject). The code from the article works fine, the properties of DynamicObject are get and set successfully from both C++ and QML, but the only thing I cannot figure out is how to fire dynamic signals.
I tried to fire "nameChanged()" signal with the following code:
bool DynamicObject::emitDynamicSignal(char *signal, void **arguments)
{
QByteArray theSignal = QMetaObject::normalizedSignature(signal);
int signalId = metaObject()->indexOfSignal(theSignal);
if (signalId >= 0)
{
QMetaObject::activate(this, metaObject(), signalId, arguments);
return true;
}
return false;
}
myDynamicObject->emitDynamicSignal("nameChanged()", nullptr);
the index of the signal is found and signalId is assigned to 5, but the signal is not fired. But if I do, for example,
myDynamicObject->setProperty("name", "Botanik");
the property is changed and the signal is fired successfully.
What is wrong in my code? What should I pass as 'arguments' parameter of QMetaObject::activate ?
EDIT1:
The full source code is temporarily available here.
A signal is also a method. You can invoke it from the meta object.
So, replace your line QMetaObject::activate(...) by:
metaObject()->method(signalId).invoke(this);
And let Qt handles the call to activate().
There is also an issue in DynamicObject::qt_metacall(): you are handling only QMetaObject::ReadProperty and QMetaObject::WriteProperty calls.
You have to add QMetaObject::InvokeMetaMethod if you want to emit your signal.

How to pass argument through signal and slots?

My GUI consists of a LineEdit and a PushButton. When the PushButton is clicked, the slot clicked() is called. I want to setup a signal-slot relationship between clicked() as the signal and doSomething() as the slot. The issue is that doSomething() does not have access to the UI and doSomething() relies on the text of LineEdit to work.
I see two solutions:
Grant doSomething() access to the UI.
I want to be able to pass a QString to doSomething() as an argument. clicked() does not accept any arguments. Qsignalmapper might be what I'm looking for. However, it seems like the mapper only passes arguments based on what the signal is. The arguments therefore need to be setup in advance and it seems like it does not help me here.
How would I do solution 2?
Assuming a C++ solution, you might consider using an intermediate signal like the following for #2:
connect(thisWidget, SIGNAL(clicked()),
thisWidget, SLOT(slotClickHandler()));
then:
void slotClickHandler()
{
QString s = "my variable string here";
emit sigDoSomething(s);
}
and them:
void slotDoSomething(const QString &s)
{
// Do something interesting with s here
}
Note that this snippet assumes you've defined a new signal sigDoSomething(const QString &) and a private slot void slotDoSomething(const QString &).

Qt: How to update the GUI when the model changes?

I use checkboxes in my GUI to toggle bits in a class controlling a maschine connected by signals/slots.
I also need the opposite direction, because if some commands to the maschine toggle the same bits, my GUI should reflect the changes.
My problem is:
When I click the checkbox, its state is not yet updated when the signal is sent.
So the first signal arrives at the maschine, the bit gets toggled, the maschine responds with the second signal and arrives at the GUI handler method 'updateCheckBoxXYZ'.
Now I want to figure out if I need to update the checkbox.
If the whole chain is started by user, the checkbox will be updated automatically at last.
If an internal command directly toggled the bit, the checkbox will need to be checked to reflect the internal change.
Because the checkbox has not been checked yet, I can not ask for 'isChecked()'.
What should I do to not getting trapped in an endless loop?
Update:
I just tried something like the following code:
// slot called by toggled(bool)
void DialogXY::checkBoxXYChanged(bool bState)
{
if (bState != m_bGuiStateXY)
{
m_bGuiStateXY = bState;
emit GuiXYChanged(bState);
// optional: .. do some GUI related things ..
}
}
// slot called on signal 'GuiXYChanged(bState)'
void Machine::changeXY(int iModul, bool bState)
{
if (bState != m_bMachineStateXY)
{
emit MachineXYChanged(bState);
}
// .. change machine configuration ..
}
// slot called on signal 'MachineXYChanged(bState)'
void DialogXY::updateCheckBoxXY(bool bState)
{
if (bState != m_bStateXY)
{
ui.checkBoxXY->setChecked(bState);
// will signal toggled()
}
}
But I need extra variables for each GUI item,
I have to initialize then correctly, etc.
I would prefer something more elegant.

QTableView, QStandardItemModel and Signals

I have a QTableView, populated with a QStandardItemModel.
I update the model frequently over network and the model is also updated by user directly via the QTableView.
Now I like to call a method when the user is changing some data, so i did:
connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(dataChanged(QStandardItem*)));
The Problem now is, that my dataChanged method is called, also when the item is updated over the network.
model->setData(index, new_val);
Is there another signal which is only emitted if, the user is changing something inside the QTableview ???
No, AFAIK there is no such signal but you there is a way to hack it.
When editing an item from the QTableView the activated signal will have been emited. The idea is to catch this signal and connect it to a slot that will store the last manually changed item.
connect(view, SIGNAL(activated(QModelIndex), this, SLOT(manuallyActivated(QModelIndex)));
void manuallyActivated(QModelIndex index)
{
// This variable should be in your header file...
lastManuallyModifiedIndex = index;
}
Now simply modify your dataChanged slot to check if the item that changed corresponds to the last modified item.
void dataChanged(QStandardItem* item)
{
// If it is invalid simply ignore it...
if (lastManuallyModifiedIndex.isValid() == false)
return;
// only if it is modified manually we process it
if (item->index() == lastManuallyModifiedIndex)
{
// make last modified index invalid
lastManuallyModifiedIndex = QModelIndex();
doSomething();
}
}
You could block the table signals when an update comes in from your network.
QObject::blockSignals(bool block)
or you could listen for click and edit event in succession.

Connecting multiple signals to a single slot in Qt

I'm trying to keep track of the textChanged() signal on for handful of QTextEdits. I want to do the same thing regardless of the text edit emitting the signal: uncheck its associated checkbox in a QListWidget if it becomes empty and leave it checked otherwise. The function I have so for is as follows:
void MainWindow::changed()
{
QString tempStr = ui->hNMRedit->toPlainText();
if(tempStr != "")
{
ui->checkList->item(0)->setCheckState(Qt::Checked);
}
else
{
ui->checkList->item(0)->setCheckState(Qt::Unchecked);
}
}
With the current approach, I would have to make a function like this for every QTextEdit; each function containing virtually identical code. If I stored each of the text edits in an array (so I could find their associated index in the QListWidget), would it be possible for me to have a slot like this?
void MainWindow::changed(QWidget *sender) // for whichever text edit emits the
// textChanged() signal
{
QString tempStr = sender->toPlainText();
if(tempStr != "")
{
// I would potentially use some sort of indexOf(sender) function on the array I
// mentioned earlier here... a little new to Qt, sorry
ui->checkList->item(array.indexOf(sender))->setCheckState(Qt::Checked);
}
else
{
// same as above...
ui->checkList->item(array.indexOf(sender))->setCheckState(Qt::Unchecked);
}
}
Is this possible or should I just create a separate slot for every text edit?
Please let me know if any further clarification is needed!
Lastly, I feel like the only meaningful difference between QLineEdits and QTextEdits is the default size. In favor of keeping things consistent, should I just use one of these objects throughout my UI?
Thanks!!!
I think you are missing the point of slots and signals. How are you creating the connections?
Are you trying to check a box when any of the text boxes change? If so use a QSignalMapper to map the textChanged() signals to send a value of true and connect that to the QCheckBox setChecked(bool) slot.
If that is too complicated subclass QCheckBox and create a set of functions checkBox() uncheckBox() so you can toggle states without a variable. Then connect the QTextEdit textChanged() to your subclass checkBox()
If this is not what you are looking for, at least subclass QTextEditto take in a QCheckBox that it can change when the text changes instead of duplicating code for every QTextEdit
All you need is a hash of QAbstractButton*, keyed by QTextEdit*. In the slot, you look up the sender() in the hash, if found you've got the button you need. This is precisely what is done by the QSignalMapper: you can map from a sender QWidget* to your button QWidget*. Use qobject_cast to cast to QAbstractButton*.

Resources