As in Qt5, there's a new signal-slot syntax and I tried utilizing it.
Firstly, I created a void return type function:
void update()
{
cerr << "complete";
}
And connected it to &QRadioButton::pressed
QRadioButton *button = new QRadioButton;
QObject::connect(button, &QRadioButton::pressed, update);
Things went fine. Afterward, I tried inserting a parameter:
void update(bool trigger)
{
if (!trigger) {return;}
cerr << "complete";
}
And tried connecting:
QRadioButton *button = new QRadioButton;
QObject::connect(button, &QRadioButton::toggled(bool), update);
But it returned error: called to non-static member function without an object argument.
I tried this as well:
QObject::connect(button, &QRadioButton::toggled(bool), update(bool));
with the same result.
So, how can I patch this? Or, more favorable, how can I implement my function as a slot and get Qt to do the job the old fashion way with SIGNAL() and SLOT()?
Sidenote: I fully understood and also tested the first case (with void return type), but I want to know if I ever have to deal with the second case (which will be a lot, actually), what would be a good option.
You don't have to set the signature since Qt will use the signature of the slot so your initial code should work:
QObject::connect(button, &QRadioButton::pressed, update);
Related
I am passing a QVBoxLayout as an argument to a method and creating controls at runtime.
QDoubleSpinBox *test; // Global variable at the top of the cpp file
void Sph::CreateUI(QVBoxLayout* layout)
{
QDoubleSpinBox *PositionXSpinBox = new QDoubleSpinBox;
test = PositionXSpinBox;
PositionXSpinBox->setRange(-10000, 10000);
PositionXSpinBox->setSingleStep(1.0);
PositionXSpinBox->setValue(40);
layout->addWidget(PositionXSpinBox);
bool ok = QObject::connect(PositionXSpinBox, SIGNAL(valueChanged(double)),
this, SLOT( ParamChange()));
}
In my current scenario I am declaring global varibles at the top of the .cpp file, for example in this case QDoubleSpinBox *test;
and in the ParamChanged function I am changing a private variable of the class .
void Sph::ParamChange()
{
this->fSegments = test->value();
this->isChanged = true;
}
1) is it possible to send the value of PositionXSpinBox in the connect signal itself.
I am not entirely sure if you asking this simple thing, but yes, slot can receive the parameter of the signal. Signal parameters wouldn't make much sense otherwise, now would they?
Something like this
void Sph::ParamChange(double value)
{
this->fSegments = value;
this->isChanged = true;
}
and this
bool ok = QObject::connect(PositionXSpinBox, SIGNAL(valueChanged(double)),
this, SLOT( ParamChange(double)));
The more modern way to do this connect would be to use the new syntax:
QObject::connect(PositionXSpinBox, &QSpinBox::valueChanged,
this, &Sph::ParamChange);
This is preferable because it will give compile time error if you for example make a typo in method names.
As a side note, if this indeed was your problem, I highly recommend going through the Qt basics, for example this: https://doc.qt.io/qt-5/signalsandslots.html
Let's say I have Widget containing a Button and a Spinbox. When the Button is clicked I wish to emit the value of the Spinbox.
I see two possible ways to do this:
Either I can create a private member function
//...
connect(m_Button, &QPushButton::clicked, this, &SomeWidget::emitSpinboxValue);
//...
SomeWidget::emitSpinboxValue() {
emit spinboxValueChanged(m_Spinbox->value());
}
Or I can directly do that in a lambda:
//...
connect(m_Button, &QPushButton::clicked, [this]() { emit spinboxValueChanged(m_Spinbox->value()) });
//...
The lambda way looks neater (since I do not need to create a rather empty member function), but on the other hand seeing that emit in the lambda gives me a bad feeling in my gut.
So, is emitting signals in a lambda ok (and my gut oversensitive), or is it bad style (or do I even set myself up for some unexpected trouble in the future)
It's fine
emit is an empty macro. A signal is a (tool generated) function, and it gets called in the same way as any other function. You've captured this, so you can call any member from the lambda as-if you were in a SomeWidget method.
It is totally fine. However, there is something wrong in your code:
connect(m_Button, &QPushButton::clicked, [this]() { emit spinboxValueChanged(m_Spinbox->value()) });
Must be:
// Important!
// vvvv
connect(m_Button, &QPushButton::clicked, this, [this]() {
emit spinboxValueChanged(m_Spinbox->value());
});
You have to specify the object context for the functor to ensures that the functor will not get invoked if the object does not exist anymore.
See this post for more information.
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.
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 &).
I have problem with one class in my project, after click appears new window with QTableWidget and QPushButton, after clicking the button I should have "test" on stdout, but nothing shows, here are parts of this code:
Header:
class ClientsSelector : public QWidget {
Q_OBJECT
public:
ClientsSelector(InvoiceTab* parent);
QWidget *window;
private:
QPushButton *accept;
public slots:
void loadData();
Constructor:
window = new QWidget();
layout = new QGridLayout();
layout->addWidget(table, 0, 0);
/*code*/
accept = new QPushButton(QString::fromUtf8("Load data"));
connect(accept, SIGNAL(clicked()), this, SLOT(loadData()));
layout->addWidget(accept, 0, 1);
/*code*/
window->setLayout(layout);
window->show();
Method:
void ClientsSelector::loadData() {
QTextStream std(stdout);
std << "test" << endl;
}
I have not even one warning nor error. I have nothing on stdout, it looks like button was connected to wrong object(?)
How do you instantiate ClientsSelector? Isn't it a singleton or global variable by chance? Try moving the connect call to a separate init function which is called after the ClientsSelector constructor. It helped me in similar WTF situations. It has something to do with the fact that each QObject inheritor has a static metadata member and you can't be sure about when it is fully initialized until the constructor finishes. connect won't work without that metadata.
See for example here: http://www.qtcentre.org/threads/9479-connect-in-constructor
If still lost, go through this checklist. Qt signals are so easy to use, everybody sometimes forgets it also has some requirements.
Seems like you forgot a closing " on the test printout.
Try using
qDebug() << "test";
instead of QTextstream
You can do following to make sure that the connection was made and slot is called.
connect function returns status of connection. Check if the connection was made properly.
put a breakpoint and see if the loadData() is called when button is pressed.
This might help to find the cause.