Can I simplify the following code for qt? - c++

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.

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.

Force QPlainTextEdit uppercase characters

I want to convert all lowercase characters as I type to uppercase in a QPlainTextEdit. In a QLineEdit I do the same via a validator, but there seems to be no validator for QPlainTextEdit.
I have tried ui->pte_Route->setInputMethodHints(Qt::ImhUppercaseOnly); but it does nothing, most likely using it wrong.
Any better option as using my "own" class?
A quick test using an event filter seems to work reasonably well...
class plain_text_edit: public QPlainTextEdit {
using super = QPlainTextEdit;
public:
explicit plain_text_edit (QWidget *parent = nullptr)
: super(parent)
{
installEventFilter(this);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
if (auto *e = dynamic_cast<QKeyEvent *>(event)) {
/*
* If QKeyEvent::text() returns an empty QString then let normal
* processing proceed as it may be a control (e.g. cursor movement)
* key. Otherwise convert the text to upper case and insert it at
* the current cursor position.
*/
if (auto text = e->text(); !text.isEmpty()) {
insertPlainText(text.toUpper());
/*
* return true to prevent further processing.
*/
return true;
}
}
}
return super::eventFilter(obj, event);
}
If it does work sufficiently well then the event filter code can always be pulled out separately for re-use.
Using event filters for such a simple task does not look like a good idea, since you are forced to implement either a separate class inheriting QPlainTextEdit or create some separate class working as a filter. Instead, you could also do the following:
// Note. This is just a sample. Assume that 'this' is context of some class (e.g. class implementing QDialog/QMainWindow)
auto lineEdit = new QLineEdit();
/*
Here, you can use also &QLineEdit::textChanged, and it would not cause any stackoverflow,
since Qt is pretty optimized here, i.e. if text does not change actually (value of QString
remains the same), Qt won't fire the signal. However, it is probably better to use
&QLineEdit::textEdited, since you expect the user to enter the text.
*/
connect(lineEdit, &QLineEdit::textEdited, this, [lineEdit](const QString& text)
{
lineEdit->setText(text.toUpper());
});
In other words, you can achieve the same behavior desired through simple signals and slots mechanism that Qt gives us. If you can achieve what you want through standard framework mechanisms, then you should try this instead of trying to implement event filter which might cause problems you might even be unaware of. Keep in mind that event filter is another mechanism provided by Qt that gives you more freedom to do what you want to do, but also you have to take of much more corner cases.
I got a problem with eventFilter method and I used a simpler solution:
protected:
void keyPressEvent(QKeyEvent* e) override {
if (!e->text().isNull() && !e->text().isEmpty() &&
e->modifiers() == Qt::NoModifier &&
e->key() >= Qt::Key_A && e->key() <= Qt::Key_Z)
{
insertPlainText(e->text().toUpper());
}
else
QPlainTextEdit::keyPressEvent(e);
}
I am using CodeEditor class from Qt examples which inherits from QPlainTextEdit.

Passing different classes in connect() in Qt

I'm working on a image editing software which includes a few classes. But I need my code to be more generic. But I've got a big problem with my classes when it comes to connections.
QObject::connect(actionSmartContrast, SIGNAL(triggered(bool)), effectsWindow, SLOT(addSmartContrast()));
QObject::connect(actionSaturation, SIGNAL(triggered(bool)), effectsWindow, SLOT(addSaturation()));
I've got a Menu called "Effects", and when the user clicks the QAction actionSmartContrast, then the effect Smart Contrast is added to my effects window. The thing is, given that each effect has its own class, I have to create a function for each class as you can see in the code above. And this is very repetitive. I would like to avoid this problem by doing something like this:
QObject::connect(actionSmartContrast, SIGNAL(triggered(bool)), effectsWindow, SLOT(addEffect(new SmartContrast())));
QObject::connect(actionSaturation, SIGNAL(triggered(bool)), effectsWindow, SLOT(addEffect(new Saturation())));
Everything would be fine for the function addEffect() because it expects a pointer to an Effect object and both SmartContrast and Saturation inherit from Effect. The only problem is that it is impossible to pass variables in connect() like this. So I thought of subclassing QAction and creating a signal which would return the class I like everytime but again, how to tell my new Action class what class it should return? If I have a thousand effects, I won't subclass QAction a thousand times! I need to create a function which would take for example a pointer to a SmartContrast object and it will guess that it has to return a SmartContrast pointer everytime the Action is clicked. And that would still be possible to do it because of the inheritance from the class Effect. But I really can't figure out how to do that. Any help would be much appreciated. Thanks in advance!
Looks like QSignalMapper is exactly what you're looking for.
UPDATED:
Another way is to use lambda (if Qt version and c++ compiler allows):
QObject::connect(actionSmartContrast, &QAction::triggered, [effectsWindow](){ effectsWindow->addEffect(new SmartContrast()) });
There are several options.
If it is enough to have the base class pointer of the effects because you use e.g. virtual methods following solution should do:
You can create an intermedite class:
class Intermediate : public QObject
{
Q_OBJECT
public:
Intermediate(QObject* parent = 0) : QObject(parent){}
signals:
void triggerEffect(Effect*);
public slots:
void effectTriggered()
{
QAction* action = qobject_cast<QAction*>(QObject::sender());
if ( action ) {
std::map<QAction*,Effect*>::iterator it = m_mapping.find(action);
if ( it != m_mapping.end() )
{ emit triggerEffect( it->second ); }
}
}
public:
void registerActionEffectPair(QAction* action,Effect* effect)
{ m_mapping[action]=effect; }
private:
std::map<QAction*,Effect*> m_mapping;
};
To use your Effect base class as type for signals and slots, you have to register it as a metatype:
qRegisterMetaType<Effect*>();
Connect it:
QObject::connect(intermediateInstancePtr, SIGNAL(triggerEffect(Effect*),
effectsWindow, SLOT(addEffect(Effect*)));
And the connections of each action would look like:
intermediateInstancePtr->registerActionEffectPair( yourEffectAction, theEffectPtr );
QObject::connect(yourEffectAction, SIGNAL(triggered(bool)),
intermediateInstancePtr, SLOT(effectTriggered()));
Another one could be to use QObjects properties:
setProperty( "property", "value" )
Call this for each effect QAction and read the property in the slot "addEffect".
The property can be read by calling
QAction* action = qobject_cast<QAction*>(QObject::sender());
if ( action ){
QVariant val = action->property("property");
if ( val.isValid() )
{
//TODO
}
}
since Object::sender returns the sender which is responsible for the slot call.
Afterwards you can do a switch case or stuff like this to distinguish between the different effects.
I finally solved my problem! I subclassed QAction and added a signal to my new class which creates a new effect from the class I want depending on the property text(). Simple if blocks are enough. Thank you all for your answers!

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.

Binding arguments to signals/slots

I basically have multiple events signals which I want to connect to the same slot. What I want to know is how can I pass string based parameters to that same slot so that the slot knows which is this signal coming from. One alternative is to make as many slots as there are signals and then connect them in a 1:1 manner, but this is efficient, considering that the code for all the processing is very similar. I tried doing this but I'm getting some errors:
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1")));
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2")));
The error is related to the parameters I'm passing in the last 2 commands .. And backgroundTypeChoiceMade is declared like this:
void backgroundTypeChoiceMade(QString);
Can someone tell me what the error is in the above code ?
You can use QSignalMapper. Although the QSignalMapper is the answer to your question, I think jon hanson's answer is the way you should take. You get much more cleaner code that way.
Four methods. One doesn't suck.
QSignalMapper. Works, but makes for messy code.
Named slots. Messy for any significant number of senders, and doesn't work for dynamically-generated senders (e.g., buttons in a list).
sender()-compare. Can handle dynamic senders, but is still kinda ugly.
Subclass the sender. Doesn't suck. Gives you what you really wanted all along: parameterized signals.
Especially when you're using a small number of signals and sender types and when the senders are dynamically generated, subclassing the sender is the cleanest way. This lets you overload the existing signals to contain whatever parameters you need.
And now, wiring up the signals and slots just works:
Keypad::Keypad(QWidget *parent) : QWidget(parent)
{
for (int i = 0; i < 10; ++i)
{
// KeypadButton keeps track of the identifier you give it
buttons[i] = new KeypadButton(i, this);
// And passes it as a signal parameter. Booyah.
connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int)));
}
createLayout();
}
void Keypad::digitClicked(int digit)
{
// The slot can find the clicked button with ease:
dial(button[i]); // or whatever
//...
}
and the extra code is out-of-sight in a subclass you'll never have to touch again.
See http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach for an example implementation of subclassing QPushButton to emit clicked(int) signals. Also discusses all four methods - named slots ("the trivial solution"), sender(), subclassing, and signal mapper.
Caveat: Obviously works best for small numbers of sender types. But that's usually the case. And in that case, it's worth it.
What is inefficient about using separate slots? If there's commonality in the slot handlers then move that into a function, e.g. extending ereOn's example:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2()));
}
void YourClass::common(int n)
{
}
void YourClass::yourSlot1()
{
common (1);
}
void YourClass::yourSlot2()
{
common (2);
}
You can't pass constants to connect() because the effective parameters are deduced at execution time, not compile time.
However, while this is against the OO principle, you can use QObject::sender() which gives a pointer to the emitter QObject.
Example below:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot()));
}
void YourClass::yourSlot()
{
if ((QPushButton* button = dynamic_cast<QPushButton*>(sender()))
{
// Now button points to a QPushButton* that you can compare with the pointers you already have
if (button == m_button1)
{
// Whatever
} else
if (button == m_button2)
{
// Whatever
}
}
}
If you have many buttons, you may also use a QSignalMapper by providing an identifier for each button.
You can now really bind a value when connecting. Qt5 added support for that.
Example:
connect(sender, &Sender::valueChanged,
tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
See more info.
NB: you can of course use std::bind or boost::bind instead of tr1::bind.
If you really don't want to use QSignalMapper, you could do something like this:
class SignalForwarderWithString: public QObject
{
Q_OBJECT
public:
SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {}
QString _data;
signals:
void forward(QString);
public slots:
void receive() { emit forward(_data); }
};
...
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
SignalForwarderWithString *sfws;
sfws = new SignalForwarderWithString("button1", this);
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
sfws = new SignalForwarderWithString("button2", this);
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
but QSignalMapper is just as easy...
QSignalMapper *mapper = new QSignalMapper(this);
connect(button1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button1, "button 1");
connect(button2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button2, "button 2");
// you might have to tweak the argument type for your slot...
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString)));