I have one c++ class and have one signal in it and want to connect that signal with slot another C++ class. Here is my code
class Data : public QObject
{
Q_OBJECT
public:
static Data *instance(void);
signals:
void sendUserID(const QString& userId);
private:
static Data *s_instance;
};
here is my Slot in another class
void DataDetails::seTUserID(const QString user_id)
{
QAndroidJniObject user_id = QAndroidJniObject::fromString(user_id);
QAndroidJniObject::callStaticMethod<void>("com/user/data/userActivity",
"setUserID",
"(Ljava/lang/String;)V",
user_id.object<jstring>());
}
The idea is to access the value of user_id from Data class to DataDetails class
The connection is trying is
QObject::connect(&s_instance, SIGNAL(sendUserID(uid), this, SIGNAL(setUserID(uid))
any other id to get uid to other class is also fine ..
Generally speaking, when you encounter an issue with QObject::connect...
Make sure you have your declarations in order:
Both classes need the Q_OBJECT macro in their declaration.
Make sure your slot is actually declared as a slot (i.e. is part of a public slot: section).
Because connecting signals and slots just uses character strings evaluated at run-time, it's possible to write absolute nonsense and have it compile. In my experience, errors caused by typos are pretty common.
Always test in an environment where you can see your application's console output. Failed connect calls will usually trigger an error message printed to stderr.
Double-check your method names and signatures. Code will still compile even if you've made a typo!
For debugging, use assertions (e.g. bool c = connect(...); Q_ASSERT(c);) to catch missed connections early.
Alternatively, you can use the QMetaMethod-based version of QObject::connect, introduced in Qt 4.8, to avoid some of these issues.
In your particular case:
You've got a typo in the function declaration: it's called seTUserID but you're using setUserID in the connect call.
You're using variable names, not function signatures, in your signal and slot names. Qt expects to see QObject::connect(&s_instance, SIGNAL(sendUserID(const QString), this, SLOT(setUserID(const QString))
You've got a signal connected to another signal, which is valid but doesn't do what you want (it's usually used to chain stuff like this: SomeChildWidget's signal -> MyClass1's signal -> MyClass2's slot).
Check that seTUserID definition is marked as slot. You are probably calling connect in some Data method (because you are using private member directly). Are you trying to use a ref to a pointer to s_instance O_o? Write sendUserID(const Qstring) (the signature of a signal) rather then sendUserID(uid). The same situation with setUserID. You are trying to connect to this pointer and want to send info to another class!? Use new style of Qt connecting signals and slots (pointers to members), with was introduced in Qt 5. Check for setUserID has written right.
Related
I'm designed a QTableWidget with QPushButton, I would like to connect these buttons with a slot to hide some rows.
I'm using a lambda expression to pass a number of a row. But the compiler doesn't recognized this expression :
connect(this->ui->tableWidget->cellWidget(i,0),&QPushButton::clicked,[this,i]{hideRows(i);});
I have this error:
error: no matching function for call to 'SoftwareUdpater::MainWidget::connect(QWidget*, void (QAbstractButton::*)(bool), SoftwareUdpater::MainWidget::displayTable()::<lambda(int)>)'
The function hideRows(int) is declared as a function. And, as a slot, it doesn't work,
CONFIG += c++11 is added in pro file,
My class MainWidget inherits from QWidget,
Q_OBJECT is added in the header.
So I don't udnerstand why connect() is not recognized by Qt 5.9.1 MinGw 32bit.
Edit: [this,i]() instead of [this](const int i) for the lambda expression
Your connection is wrong. You can't connect a function that doesn't take parameters (clicked()) with a function that takes parameters (your lambda). To verify that this is the case, just do this:
connect(this->ui->tableWidget->cellWidget(i,0),&QPushButton::clicked,[this](){});
And see that it will compile. You have to make your design in such a way that signals and slots are compatible.
Also avoid using lambdas in signals and slots. Read the caveats here.
I was reading your comments on the accepted answer and noticed the root of the problem: This error is being thrown because the effective type of the object — as supplied to QObject::connect; i.e QWidget in your case — does not define the referenced signal QPushButton::clicked.
What likely happened is that the QPushButton pointer was cast into a QWidget and then that pointer was given to connect instead of the original which defines the signal.
Cast the pointer back to a QPushButton * and the error should go away.
I have a buttongroup defined with two radiobuttons
buttonGroupFFTDimension = new QButtonGroup(this);
buttonGroupFFTDimension->addButton(ui->radioButton1D, 1);
buttonGroupFFTDimension->addButton(ui->radioButton2D, 2);
buttonGroupFFTDimension->setExclusive(true);
ui->radioButton1D->setChecked(true);
The connect also compiles
connect(this->buttonGroupFFTDimension, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked),
this, &MainWindow::on_buttonGroupFFTDimension_buttonClicked);
but it throws and error at runtime
QMetaObject::connectSlotsByName: No matching signal for on_buttonGroupFFTDimension_buttonClicked(int)
I admit that I am not familiar with the new connect syntax, but also do not see the obvious error. What is wrong?
The message shown is because you are using Qt Designer and it uses the connectSlotsByName method to connect various elements, it recognizes the format on_somesender_somesignal, and in your case matches your slot.
First solution: It iss unnecessary to use the connect function, this will automatically do it. Also I think that the slot does not have as parameter the type int that requires.
In your case the slot should be as follows:
private slots:
void on_buttonGroupFFTDimension_buttonClicked (int val);
Another possible solution is to rename the slot, after that you run make clean and qmake.
I have a block of spin controls which change individual elements of an array
Rather than having separate receiver slot functions, I wanted to just specify which control sent the message in the signal
You can do this with a QSignalMapper - but is there anyway of doing it simply as below?
spin0 = new QDoubleSpinBox;
connect(spin0,SIGNAL(valueChanged(double)),this,SLOT(handler(0,double));
spin1 = new QDoubleSpinBox;
connect(spin1,SIGNAL(valueChanged(double)),this,SLOT(handler(1,double));
....
private slot:
void handler(int element,double value);
From any slot handler you can can use sender() to get a pointer to the object that sent the signal. Then you can use the objectName() property to communicate any further identifying information.
I don't believe so, at least not using that syntax ... the SIGNAL and SLOT macros turn their arguments into strings which are then parsed and used by the Qt runtime to look-up the associated functions and/or class methods in the tables created by moc during the pre-processing phase of compilation. So if you encoded a default argument into the SLOT macro, then that's not a valid function signature that can be used by Qt for run-time lookup of the actual slot function in the moc-generated function tables.
I have an app with such structure: all the datatypes (class INode) are stored in plugins (DLLs). Some of the datatypes can be drawn (if they're IDrawable).
To load an object of, e.g. class PointCloudNode: public INode I have a special input plugin (DLL) which is called class PointCloudParser: public IIOPlugin and IIOPlugin is a thread with some specific functionality: class IIOPlugin: public QThread.
All the objects are created by NodeFactory class which is a singleton stored in separate DLL.
And here's the problem:
void PointCloudNode::update()
{
QObject::connect (this,SIGNAL(tmptmp()),this,SLOT(drawObject()));
emit tmptmp();
}
If I do this from any thread (main thread or the Input Plugin thread)
NodeFactory* fab = NodeFactory::getInstance();
boost::shared_ptr<INode> pc(fab->createNode("pointCloud","myPC"));
boost::shared_ptr<IDrawable> dr = boost::dynamic_pointer_cast<IDrawable>(pc);
dr->update();
The update launches, the tmptmp() signal is emitted, and the slot (drawObject()) executes correctly.
BUT
if do just the same, but create the object in my Input Plugin, pass over the shared pointer and execute dr->update() in another function, the slot drawObject() is never entered though all the code is executed (including connect, etc.).
To be more precise, here's the Input Plugin:
void PointCloudParserPlugin::doLoad(const QString& inputName, boost::shared_ptr<INode> container)
{
NodeFactory* factory = NodeFactory::getInstance();
boost::shared_ptr<INode> node = factory->createNode("pointCloud", inputName);
// here goes the loading itself, nothing special...
container->addChild(node); //that's the container where I keep all the objects
//boost::dynamic_pointer_cast<IDrawable>(container->getChild(inputName))->update();
//If I uncomment this line, it all works: the slot is launched.
emit loadingFinished(inputName); // it executes the following function
}
The last emit is connected to this:
void GeomBox::updateVisualization(const QString& fileName)
{
boost::shared_ptr<INode> node = container_->getChild(fileName);
boost::shared_ptr<IDrawable> nodeDrawable = boost::dynamic_pointer_cast<IDrawable>(node);
nodeDrawable->update(); //this is the problem line: update() executes, connect() works, but the slot never runs :(
}
How come? The node object is the same all the way through, it is valid. Every line in code in launched, QObject::connect doesn't write anything to debug window, the signal tmptmp() is emitted, but the slot drawObject() in one case is never reached? Any ideas?
Upd.: If I do not inherit IIOPlugin from QThread, everything works fine (i.e. load the object in the main thread). I expected the signals/slots to work across the threads...
Since you are sending a signal across to a different thread, you might need to explicitly tell Qt that the connection should be a queued one:
QObject::connect(this, SIGNAL(tmptmp()), this, SLOT(drawObject()), Qt::QueuedConnection );
By default Qt will use Qt::AutoConnection as that last parameter, and it will choose whether to use a direct connection (if the slot is in the same thread as the emitter) or a queued connection (if the slot is in a different thread). But since your thread is in a separate library, maybe Qt isn't making the right assumption here.
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 ?