Qt/C++ Elegant way for comparison based signal dispatching - c++

I am working on a project where I receive a message via UDP and based on that message I am emitting different signals and all of them have the same parameters.
The structure is like this:
if(command_type == COMMAND_TYPE_MOVE)
{
emit sigMoveForward(data);
}
else if(command_type == COMMAND_TYPE_STOP)
{
emit sigStopMove(data);
}
This gets really tedious to programm and maintain when getting past like 10 commands. Is there a way to do this better?
I thought of creating a QMap and doing a lookup on it and emiting the signal I am getting. Is this possible in Qt to have a pointer to the function and omit it this way?

There is no difference between
emit sigMoveForward(data);
and
sigMoveForward(data);
So, you can create QMap with pointers to signals or beter plain array if commands are continuous. It has quite tricky syntax, though.
void (Your_class::*signals[COMMAND_COUNT])(your_data_type) ;
...
signals[COMMAND_TYPE_MOVE] = &Your_class::moveForward;
signals[COMMAND_TYPE_STOP] = &Your_class::stopMove;
....
And emit it like that:
(this->*signals[command_type])(data);

If all signals contain the same data, you could send a unique signal containing 2 parameters (the command_type + the data) and then check the command_type in the receiving slot
//...
emit sigCommand(command_type, data)
//...
void MyClass::commandReceived(CommandType command_type, QVariant data) {
if(command_type == COMMAND_TYPE_MOVE)
{
moveForward(data);
}
else if(command_type == COMMAND_TYPE_STOP)
{
stopMove(data);
}
//...
}

Related

Qt: Use functors as sender in connect(...)

I am new to Qt and I wonder how to connect a functor to a slot properly.
This is basically what I tried:
std::function<void ()> sender_function = std::bind(SenderClass::senderFunction, sender);
...
connect(sender, &sender_function, this, &ThisClass::SomeFunction);
However, it does not work. Can someone point me in the right direction?
Thanks and best regards,
Alex
Edit:
Use case is the following. I have a set of ~50 parameters fed by sensors in a fixed frequency. To plot the data individually, I have a signal for every parameter which is called when an update arrives. These signals I store in a vector in a structured way, so I don’t need to perform lookups for every parameter (updates can come with 100-1000 Hz).
Now my idea was, that plots can dynamically connect to the parameter signals, by performing a look up once on connection (parameter name -> signal as std::function).
Edit 2:
Because there were further questions, here some code examples. I hope this makes it clearer. The signals for parameter updates look like this:
signals:
void DataController::port000(double timestamp, Parameter_t p);
Port000 is of course an example, I have 50 more like that, so that every parameter can be individually sent through my program for plotting etc. Next I created a vector of my signals:
using PortUpdateFunc = std::function<void (double, Parameter_t)>;
QVector<PortUpdateFunc> _port_update_functions;
_port_update_functions[0] = std::bind(&DataController::port000, this, ph::_1, ph::_2);
This allows me to easily just call _port_update_functions[30](stamp, parameter) and it will send the parameter that I linked with that port on its way. This is the sender side. Now on the slot side I have something like this:
switch(port_id)
{
case 0: QObject::connect(sender, &DataController::port000, this, &SomeSlotFunction); break;
case 1: QObject::connect(sender, &DataController::port001, this, &SomeSlotFunction); break;
}
While this works perfectly fine, I have to explicitly put all the ports in my switch-case handle. And if I have 50 parameters thats 50 cases with port000 to port049, which is a bit of type work. What I thought of doing was something like this:
DataController::PortUpdateFunc port_functor = _port_update_functions[port_id];
QObject::connect(sender, &port_functor, this, &SomeSlotFunction);
So I could replace the port000, port001, ... case-handle by just getting the functor from the initial _port_update_functions vector with the appropriate port_id. But Qt doesn't like that idea.
If you are using 001, 002 variable/method names, most likely you are doing something wrong. Also, it is better that a Plot class does not know about specific ports and does not deal with the selection and connection / disconnection of signals.
For the example you wrote, you can easily use universal signals' signature like this:
void DataController::portData(int number, double timestamp, Parameter_t p);
or just
void DataController::portData(QVariant var);
or use interface:
struct IParam
{
...
virtual QVector<QPointF> toVector() const = 0;
}
struct ConcreteParamName : public IParam
{
double timestamp;
Parameter_t p;
...
QVector<QPointF> toVector() const override
{
...
}
}
And then emit portData(1, timestamp, p);
or emit portData(QVariant::fromValue(param));
or
emit portData(sharedPointerToParam);
...
void SomeController::portDataSlot(QSharedPointer<IParam> param)
{
// decide what to draw here:
if(thePortNeedToDraw)
{
QVector<QPointF> curve = param->toVector();
plot->drawCurve(curve);
}
}
// or decide what to receive here:
void SomeController::switchSender(int number, bool enabled)
{
// without boilerplate switch-case
if(enabled)
{
QObject::connect(senders[number], &DataController::portData, this, &SomeController::portDataSlot);
}
else
{
QObject::disconnect(senders[number], &DataController::portData, 0, 0);
}
}
This is not possible. The signal argument is either a string or a PointerToMemberFunction. Functors can only be used as target. See https://doc.qt.io/qt-5/qobject.html#connect-5.

Value of spin box, just before it changes

I'm writig a code using qt libraries, in which I need to get the value of a spin box (by a signal) just before it changes.
I've got:
QSpinBox spinBoxWidth:
QSpinBox spinBoxScale;
I want to connect a signal from spinBoxWidth to spinBoxScale, so that the value of SpinBoxScale is always "the Value of SpinBoxWidth after changing" to "its value before changing".
(Scale = width_new/width_old)
I didn't find any slot in Qt which returns the old value of a spin box while changing the value. Can I somehow write a slot for that?
Best Regards
There are two ways of doing this:
Catch the change before it happens and store the old value using the event system (QKeyEvent, QMouseEvent). This is error-prone, as the value of spinBoxWidth can be set manually.
Connect spinBoxWidth's valueChanged(int) signal to a slot and reference the last value it was called with. I recommend this method.
Try something like this:
class MonitoringObject : public QObject
{
Q_OBJECT
int lastValue;
int currentValue;
...
public Q_SLOTS:
void onValueChanged(int newVal)
{
lastValue = currentValue;
currentValue = newVal;
if (lastValue == 0) //catch divide-by-zero
emit ratioChanged(0);
else
emit ratioChanged(currentValue/lastValue);
}
Q_SIGNALS:
void ratioChanged(int);
After your signals are connected, the flow should look like this:
spinBoxWidth emits valueChanged(int)
MonitoringObject::onValueChanged(int) is invoked, does its work and emits ratioChanged(int)
spinBoxScale receives the signal in its setValue(int) slot and sets the appropriate value.
The easiest way is probably a lambda that caches the value of valueChanged for the next call:
auto const width = new QSpinBox();
width->setValue(200);
connect(width, &QSpinBox::valueChanged,
[prev_value = width->value()](int const value) mutable {
auto const scale = double(value) / double(prev_value);
// do stuff
prev_value = value;
});
I believe there is no specific signal to the "value before change" because you can always store it from the previous signal "onValueChanged()" you received.
So the basic idea would be:
First time, receive signal onValueChanged(value) and store the value value_old;
Next time you receive the signal, you can compute you scale!value/value_old;
Then you can send a new signal, or directly modify the object with the new value.
You can derived your own version of QSpinBox including this code or implemented in the class it has to receive the signal. It depends on your architecture.

Can I simplify the following code for qt?

I need to simplify validations on qlineedit, which invokes a function that returns a capital letter when I'm typing. I have this:
void dg_cliente::on_lineEdit_4_textChanged(const QString &arg1)
{
Cls_Validaciones *Valido = new Cls_Validaciones;
ui->lineEdit_4->setText(Valido->Validar_Mayuscula(arg1));
}
The code is the very similar for lineEdit5, lineEdit6, lineEdit7, etc., which is redundant. Is there a better way to do this that removes the redundancy?
I interpret your question as:
How can I recognize which widget is an actual signal sender?
By calling sender() function in your slot.
void DlgClient::onLineEditTextChanged(const QString &arg1)
{
QLineEdit* pLineEditSender = qobject_cast<QLineEdit*>( sender() );
if (pLineEditSender) // also verify that is required type of sender
{
// FYI: setText also signals textChanged
// make sure the code is not looping here
// so bool m_forcedSetText initially set false
if ( ! m_forcedSetText)
{
m_forcedSetText = true;
pLineEditSender->setText( myTransform(arg1) );
}
else
m_forcedSetText = false;
}
}
P.S. Maybe the other type of solution as suggested in comments is better? But the answer is explicit to what you ask. The info on sender() does warn that the OOP principle of modularity is violated etc. while in certain cases the function is still useful.

Prevent Firing Signals in Qt

We have a QCheckBox object, when user checks it or removes check we want to call a function so we connect our function to stateChanged ( int state ) signal. On the other hand, according to some condition we also change the state of QCheckBox object inside code, and this causes the unwanted signal.
Is there any way to prevent firing signal under some conditions?
You can use the clicked signal because it is only emitted when the user actually clicked the check box, not when you manually check it using setChecked.
If you just don't want the signal to be emitted at one specific time, you can use QObject::blockSignals like this:
bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);
The downside of this approach is that all signals will be blocked. But I guess that doesn't really matter in case of a QCheckBox.
You can always block signal emission on QObjects using QObject::blockSignals(). Note that to be correct about things, you should remember the old state (returned from the function call), and restore it when you are done.
At my job, we prefer RAII for this sort of thing. A simple class to do so might look like this:
class SignalBlocker
{
public:
SignalBlocker( QObject *obj ) : m_obj( obj ), m_old( obj->blockSignals( true ) )
{
}
~SignalBlocker()
{
m_obj->blockSignals( m_old );
}
private:
QObject *m_obj;
bool m_old;
};
Edit: Starting with Qt 5.3, see QSignalBlocker (h/t to HappyCactus in comments)
While learning Qt, I ran into this problem with a set of interconnected widgets that I wanted to update "atomically". I liked #cjhuitt's solution, but found that it goes even better with a bit of syntactic sugar based on proxy objects. Here's the approach that I used...
First, I defined a class template for a blocker proxy object. Like Caleb's, this blocks the signals on construction, and then restores their previous state on destruction. However, it also overloads the -> operator to return a pointer to the blocked object:
template<class T> class Blocker {
T *blocked;
bool previous;
public:
Blocker(T *blocked)
: blocked(blocked),
previous(blocked->blockSignals(true)) {}
~Blocker() { blocked->blockSignals(previous); }
T *operator->() { return blocked; }
};
Next, I defined a small template function to construct and return a Blocker:
template<class T> inline Blocker<T> whileBlocking(T *blocked) {
return Blocker<T>(blocked);
}
Putting this all together, I'd use it like this:
whileBlocking(checkBox)->setChecked(true);
or
whileBlocking(xyzzySpin)->setValue(50);
This gets me all the benefits of RAII, with automatically paired blocking and restore around the method call, but I don't need to name any wrapper or state flags. It's nice, easy, and pretty darn foolproof.
You can QObject::disconnect to remove the corresponding signal-slot connection and can QObject::connect again once you are done...
In QObject derived classes, you can call blockSignals(bool) to prevent the object from emitting signals. So for example:
void customChangeState(bool checked)
{
blockSignals(true);
ui->checkBox->setCheckState(Qt::Checked);
// other work
blockSignals(false);
}
The above method would change the check state without clicked, stateChanged, or any other signals being emitted.
Qt5.3 introduced the QSignalBlocker class that does exactly what needed in an exception safe way.
if (something) {
const QSignalBlocker blocker(someQObject);
// no signals here
}
Even in QT5, its a bit cumbersome when there are many/several things to block. Here's a multi-object version that is concise to use:
class SignalBlocker
{
public:
SignalBlocker(QObject *obj)
{
insert( QList<QObject*>()<<obj );
}
SignalBlocker(QList<QObject*> objects)
{
insert(objects);
}
void insert(QList<QObject*> objects)
{
for (auto obj : objects)
m_objs.insert(obj, obj->signalsBlocked());
blockAll();
}
void blockAll() {
for( auto m_obj : m_objs.keys() )
m_obj->blockSignals(true);
}
~SignalBlocker()
{
for( auto m_obj : m_objs.keys() )
m_obj->blockSignals( m_objs[m_obj] );
}
private:
QMap<QObject*,bool> m_objs;
};
usage:
void SomeType::myFunction()
{
SignalBlocker tmp( QList<QObject*>()
<< m_paramWidget->radioButton_View0
<< m_paramWidget->radioButton_View1
<< m_paramWidget->radioButton_View2
);
// Do more work, ...
}
When some UI element should not respond to user it is appropriate to disable it. So that user would know that this element is not accepting input.

Updating pointer using signals and slots

I am very new to Qt; please help me to solve the problem.
I am using a thread to perform intensive operations in the background. Meanwhile I want to update the UI, so I am using SIGNALS and SLOTS. To update UI I emit a signal and update UI.
Let us consider below sample code,
struct sample
{
QString name;
QString address;
};
void Update(sample *);
void sampleFunction()
{
sample a;
a.name = "Sachin Tendulkar";
a.address = "India"
emit Update(&a);
}
In the above code we are creating a local object and passing the address of a local object. In the Qt document, it says that when we emit a signal it will be placed in the queue and late it will be delivered to the windows. Since my object is in local scope it will be delete once it goes out of the scope.
Is there a way to send a pointer in a signal?
You're insisting on doing the wrong thing, why? Just send the Sample itself:
void Update(sample);
//...
sample a("MSalters", "the Netherlands");
emit Update(a);
Unless you've determined that this code is a performance bottleneck you would be better to just pass a copy of the object rather than a pointer.
Really, I mean it.
However, if you must use pointers then use a boost::shared_ptr and it will delete itself.
void Update(boost::shared_ptr<sample> s);
void sampleFunction()
{
boost::shared_ptr<sample> a = boost::shared_ptr<sample>(new sample());
a->name = "Sachin Tendulkar";
a->address = "India"
emit Update(a);
}