How to transfer parameter from constructor into slot? - c++

I am doing a small exercise with Qt. As I know, slot save_clicked brings only type of parameter. So how can I transfer userid into slot save_clicked in file MyClass.cpp for processing below ?
MyClass.h
class MyClass : public QWidget
{
Q_OBJECT
public:
MyClass(QString userid, QString lastname, QString firstname, QWidget *parent = 0);
~MyClass();
public slots:
void save_clicked(QString userid);
private:
Ui::MyClass ui;
};
#endif // MYCLASS_H
MyClass.cpp
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
...........
...........
connect(ui.save, SIGNAL(clicked()), this, SLOT(save_clicked(QString)));
}
void MyClass::save_clicked(QString userid)
{
// process userid here
}

First of all, there have already been suggestions that userid doesn't need to be passed to a slot. Declaring it as a member of MyClass and simply using it in MyClass::save_clicked() will do just fine.
If you still want to know how to handle the case where you need to pass arguments to a slot not provided by the signal, there are mutiple ways to achieve that and they depend on the version of Qt you're using.
In general, the following from the Qt5 docs holds for Qt5 and Qt4:
The signature of a signal must match the signature of the receiving
slot. (In fact a slot may have a shorter signature than the signal it
receives because it can ignore extra arguments.)
Given that fact, you have to either use what the signal provides (or don't - depends on if you need it at all) or get your data somewhere else. A slot is just a regular function (a little more constrained in Qt4 but in Qt5, you can basically connect anything that's callable to a signal) and in that function, you can get the data you need from anywhere your design permits. The member variable approach is common, but you can also call another member function of any other object, free function or simply compute whatever you need inside the slot.
Note: Handling a signal doesn't necessarily mean you need any data - it's common, yes, but you could also do something simple as bring up a QMessageBox with some hard-coded text or simply clear some text edit (think something like a button doing Clear Console) or execute some external process or what have you.
If you need your slot to actually receive data when it is invoked, you have multiple options:
use everything or a subset of what the signal provides
use QSignalMapper to establish a mapping with limited control over your slot arguments
use the, IMHO, superior approach of using a function object that stores what you need, i.e. either a functor you provide directly or as a lambda or a function object returned from std::bind
For the latter to work you need C++11support , but by now, that shouldn't be a problem.
In your above example, you could do something like this in the most basic approach (UNTESTED - Please report compile errors so I can fix the answer):
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
...
connect (
ui.save,
&QPushButton::clicked,
std::bind (&MyClass::save_clicked, this, userid)
);
...
}
I the above case, when clicked() is emitted by ui.save, the function call wrapper returned by std::bind will be call, which in turn will call MyClass::save_clicked on this instance of MyClass and pass userid to save_clicked().
The above is basically what you wanted to achieve initially. However, this is just the tip of the iceberg. You can go much, much further with this and, for instance, bind anything you want.
For instance, suppose you connected to another signal provided by your button, toggled(bool checked). If you wanted to receive both the check status as a bool and a number of arguments you provide yourself, for instance the userid, you can extend the above call to std::bind with an appropriate placeholder:
connect (
ui.save,
&QPushButton::toggled,
std::bind (&MyClass::save_clicked, this, _1, userid /*, ... more args here*/)
);
You would now have the following signature for save_clicked():
save_clicked(bool, QString);
On emission of toggled(bool) by ui.save, the function call wrapper returned by std::bind will then call MyClass::save_clicked with both the bool in place of _1 and the QString you provided as the 4th argument to std::bind.

Better than use signals/slots just use a variable. It's the goal of a class. Add something like :
private:
QString userId;
And in your constructor add :
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this.userId=userid;
}
Like this you can use userId like you want and where you want into your class.
EDIT :
add in your .h
private slots:
void on_saveButton_clicked();
and in your .cpp
void MyClass::on_saveButton_clicked()
{
// you can use userId here
}
If you use Qt Creator those functions can add automatically in the UI design interface

Related

QComboBox signal not trigged

I have checked my code several times and i still can't get why it is not working.
I use a QComboBox connected to a slot in the class like this :
this->choixCam = new QComboBox;
this->choixCam->addItem("Camera 1");
this->choixCam->addItem("Camera 2");
this->choixCam->addItem("Camera 3");
this->choixCam->addItem("All cameras");
QObject::connect(this->choixCam, SIGNAL(currentIndexChanged(int)), this, SLOT(this->selectCam(int)));
This previous part of code is defined is the constructor of my class MainWindows, called in the main. The definition in the header file is the following :
public:
QComboBox* choixCam;
public slots:
void selectCam(int choixCam);
I tried with successfully to run the slot from another signal.
Using the signal with QString, the signal activated(int) or trying an exemple find on the net didn't work neither. Signals/slots mecanism also work for QButton and QSpinBox.
I am running out of idea. Some help would be very appreciate.
Thank you.
#eyllanesc answer should work. Just change SLOT(this->selectCam(int)) to SLOT(selectCam(int)).
But why isnt it the same for QT?
Lets have a look at the connect method:
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,const QObject *receiver, const char *method,
Qt::ConnectionType type)
(https://github.com/qt/qtbase/blob/e4c39d5e1e7ee8c2bba273e6d613ec519b7fa9c2/src/corelib/kernel/qobject.cpp#L2659)
and at the SIGNAl and SLOT definition:
#define SLOT(a) "1"#a
#define SIGNAL(a) "2"#a
QT uses c-strings to identify the signals and slots for qobjects.
These strings are used as keywords in some kind of dictionary over all qobjects, signals and slots.
Just try std::cout << SIGNAL(some text) << std::endl; to see what SIGNAL and SLOT do.
Thats why can call connect even without SIGNAL and SLOT:
connect(this->choixCam, "2currentIndexChanged(int)", this, "1selectCam(int)");
Now you know that
SLOT(this->selectCam(int)) will produce "1this->selectCam(int)" as keyword instead of "1selectCam(int)"
The SIGNAL and SLOT definitions are used because most IDEs disable C++ autocompletion inside quotation marks, which makes it hard to write the correct function signature.

Qt: How to pass variable value betweeen QWizardPages with registerField()

I'm working on Qt 4.8.5. I'm using a QWizard structure with its QWizardPages (lets name them wp1, wp2, wp3,...). I need to pass one value from wp2 to wp4 but every time I try it, I get an empty string :(
The value I need is on a variable (QString sVar;) so not a widget and I've tried some things:
Using RegisterField with the wizardpage itselfs (as its still a type of qwidget) like this: registerField("myField",this); but ofcourse when i go to wp4 and try to qDebug()<< "data: " << field("myField").toString();it is empty.
I've see in some forums ppl saying that you can create a Q_PROPERTY and then use the register field. I've set it as Q_PROPERTY sData READ getData() WRITE setDATA() and then with registerField("myfield, this, ...and here I have a problem because i expect sData to apear but it doesn't.
So... any idea about how can I achieve this using registerField (I know I can also create my own slot and signal, emit it from wp2 and catch it up on wp4 but I would like to avoid it if possible)
Added the solution:
Class A.h:
class ClassA: public QWizardPage
{
Q_OBJECT
Q_PROPERTY(QString sAP READ getAP WRITE setAP)
....
public:
QString getAP() const {return AP;}
void setAP(QString s){AP=s;};
private:
QString AP;
Class A constructor:
registerField("AP_field",this, "sAP", SIGNAL(APChanged()));
Class A ::initializePage() function:
switch(m_iVar)
{
case 0 :...
break;
case 1:
setAP("AP1");
emit APChanged();
break;
}
And then in Class B (Where you need to know that data):
qDebug() << " AP QPROPERTY = " <<field ("AP_Field").toString();
According to the docs:
When we create a field using QWizardPage::registerField(), we pass a
unique field name and a widget. We can also provide a Qt property name
and a "changed" signal (a signal that is emitted when the property
changes) as third and fourth arguments; however, this is not necessary
for the most common Qt widgets, such as QLineEdit, QCheckBox, and
QComboBox, because QWizard knows which properties to look for.
So you still need a signal, but Qt will handle necessary connections for you and will catch your new value as you change it. You have to register like this:
registerField("myField", this, "myProperty", SIGNAL(myPropertyChanged()));
Then you have to remember to emit the signal each time you change your variable, and of course register it as a property.
This works from some but not all widgets that emit a signal. For QDoubleSpinWidgets,
QWizard::setDefaultProperty("QDoubleSpinBox", "value", SIGNAL(valueChanged(double)));
This is because the valueChanged() is for QString and double....does not know which value to take without the parameter specifying:
"Every time the value changes QDoubleSpinBox emits two valueChanged() signals, one taking providing a double and the other a QString. The QString overload provides the value with both prefix() and suffix(). The current value can be fetched with value() and set with setValue()." doc.qt.io/qt-5/qdoublespinbox.html I hope this never troubles others and this complete solution, no warnings, errors nor unpredictable behaviour.

Qt: Connecting multiple input widgets with valueChanged(int) signals to single slot?

With Qt 4.8 I create a number of input widgets (QSpinBox, QSlider) programmatically.
In the end, I would like to have a single method to handle changes of any of these input widgets, ideally by index.
However, these widgets only have a Signal with parameter, e.g. valueChanged(int).
This is not compatible with QSignalMapper()'s Slot map().
As it was pointed out in the comments, the connection does work!
connect( spinbox, SIGNAL( valueChanged(int) ),
signalMapper, SLOT( map() )
);
Now I just need to get the value, but this cannot be done via the sender() method anymore, because this now is the SignalMapper.
Original question:
Is there another way besides (re)implementing QSignalMapper with additional parameters or a parameter-less valueChanged() for the widget or using objectName and QObject::sender() in order for the Slot to see which element changed (and get the new value)?
You can use QAbstractSpinBox::editingFinished() and QAbstractSlider::sliderReleased() as your signals, they are parameterless.
Unfortunately there is no parameterless version of QAbstractSlider::valueChanged() so if you want a signal emitted continuously as the slider moves, you may need to subclass QSlider and create it. E.g.
class MySlider : public QSlider
{
...
private slots:
void HandleValueChanged(int) { emit valueChanged(); }
signals:
void valueChanged();
};
MySlider::MySlider(...)
{
connect(this, SIGNAL(valueChanged(int)), this, SLOT(HandleValueChanged(int)));
}
Though I admit, this may not be the most elegant solution.
If I understand your question and what you have until now correctly, you are missing two parts.
mapper->setMapping(<spinbox>, <id or name or pointer to the spinbox);
connect(mapper, SIGNAL(mapped(<datatype you used in setMapping()>),
this, SLOT(HandleValueChanged(<datatype you used in setMapping()>)));
So the HandleValueChanged() slot will receive an identifier of your sender, then you can directly access the value of the sender with the appropriate getter.
The method setMapping() takes either and integer, QString or a pointer to the widget itself as second argument. This is then forwarded via the mapped() signal of the mapper, so you can later identify which widget emitted the signal.

How do I get a signal to call a function with certain arguments?

I want to get a signal to call a function with certain arguments, like the example below.
QPushButton *yes = new QPushButton("Yes");
yes->connect(yes, SIGNAL(clicked()), NULL, SLOT(printf("You pressed a button")));
How do I accomplish this?
An often overlooked way to reverse signal/slot relationships is QObject::sender. You can call it in the receiving slot to get a handle on the QPushButton (using qobject_cast) and get the text from there. Alternatively you can use QSignalMapper to augment signals.
It seems very inefficient but you could create a new signal with a QString argument, which you connect to your pushbutton. The text contained will be defined on your emit call.
eg.
connect(yes, SIGNAL(clicked()), this, SLOT(emitHelloWorldText());
connect(this, SIGNAL(emitText(QString)), receiver, SLOT(dostuffWithText(QString)));
then your emitHelloWorldText method can be something like
void emitHelloWorldText() {
emit emitText("Hello world");
}
Then this can be picked up by your receiver class
void doStuffWithText(const QString &text) {
Unfortunately, the slot and the signal must have matching arguments. If you really need to stick with this interface, you could create an intermediary slot to propagate the received signal, but there’s no real way around.

How to use QMetaMethod with QObject::connect

I have two instances of QObject subclasses and two QMetaMethod instances of signal in one of objects and slot in another object. I want to connect this signal and slot with each other.
I've looked through the qobject.h file and find that SIGNAL() and SLOT() macro are just add "1" or "2" character to the beginning of method signature so it looks like it should be possible to add the same character to the beginning of string returned by QMetaMethod::signature() but this approach depends on some undocumented internals of toolkit and may be broken at any time by a new version of Qt.
Does anybody know reliable way to connect signals and slots through their QMetaMethod reflection representation?
Edited:
I've created suggestion in Qt issue tracker:
https://bugreports.qt.io/browse/QTBUG-10637
If anybody also interested in this feature you can vote for this ticket there.
This has been fixed as of Qt 4.8.0:
https://bugreports.qt.io/browse/QTBUG-10637
Suppose we have a QObject* m_subject, and wish to connect the change-notification signal of a property to a propertyChanged() slot:
const QMetaObject* meta = m_subject->metaObject();
QMetaProperty prop = meta->property(meta->indexOfProperty("myProperty"));
if (prop.hasNotifySignal()) {
QMetaMethod signal = prop.notifySignal();
QMetaMethod updateSlot = metaObject()->method(
metaObject()->indexOfSlot("propertyChanged()"));
connect(m_subject, signal, this, updateSlot);
}
I successfully used this to make a QWidget subclass which finds all the properties of any QObject and creates a QLineEdit for each of them, with a connection to keep the QLineEdit updated whenever the corresponding property changes. (Because I didn't find a way to pass a propertyID value to propertyChanged() though, it was necessary to make a subclass of QLineEdit and implement propertyChanged() there. QSignalMapper didn't help, because all the properties are in the same object.)
Thanks to MBack, I now use metamethods to connect my view to my model's properties dynamically for MVVM or MVC pattern.
In order to respect DRY, a boilerplate is required with something like this :
void MyClass::connectSignalToSlot(QObject* sender, std::string signalName, QObject* receiver, std::string slotName)
{
int sigIdx = sender->metaObject()->indexOfSignal(signalName.c_str());
auto signal = sender->metaObject()->method(sigIdx);
int slotIdx = receiver->metaObject()->indexOfSlot(slotName.c_str());
auto slot = receiver->metaObject()->method(slotIdx);
connect(sender,signal,receiver,slot);
}
void MyClass::connectPropertyChangedToSlot(QObject* sender, std::string propName, QObject* receiver, std::string slotName)
{
int sigIdx = sender->metaObject()->indexOfProperty(propName.c_str());
auto signal = sender->metaObject()->property(sigIdx ).notifySignal();
int slotIdx = receiver->metaObject()->indexOfSlot(slotName.c_str());
auto slot = receiver->metaObject()->method(slotIdx);
return connect(sender, signal, receiver, slot);
}
It looks like there is no way to make it work without relying on internal implementation. If I were you, I'd submit feature request to Qt bug tracker, write a code that mimics current behavior SIGNAL/SLOT macros and add unit test that will fail when SIGNAL/SLOT behavior changes.
There might be a simpler solution to the problem you're trying to solve: describe what exactly are you trying to do without any implementation details.
If signature method is public in QMetaMethod then the result shouldn't be broken by trolls and it's safe to use it (documentation says nothing about "dangers" when using QMetaMethod::signature method). I think you can safely use it. Just to be sure, what version of Qt you are using right now ?