Private QT signal - c++

I have a data model that I want other objects to be able to watch for updates, but I don't want to give anyone control of the update signal itself. I've come up with a something that makes sense to me conceptually, but it doesn't seem to work. I'm wondering if anyone could explain why I will never get it to work, or if i'm missing something that could make this work. Effectively I have a Client class (QObject) that has an arbitrary slot and Model class that has a private signal.
important Client class code (public SLOT):
void client::doUpdate()
{
std::cout << "HELLO UPDATE" <<std::endl;
}
Model code:
void Model::unRegisterForUpdates(const char* qt_slot_handle, QObject* o)
{
QObject::disconnect (this, SIGNAL( updateHandles() ),
o, qt_slot_handle);
}
void Model::registerForUpdates(const char* qt_slot_handle, QObject* o)
{
QObject::connect( this, SIGNAL( updateHandles() )
, o, qt_slot_handle
, Qt::UniqueConnection);
}
Main func:
Model foo;
client * cl = new client();
client * cl2 = new client();
std::cout << SLOT(cl->doUpdate()) << std::endl;
std::cout << SLOT(cl2->doUpdate()) << std::endl;
foo.registerForUpdates( SLOT(cl->doUpdate()) , cl);
foo.registerForUpdates( SLOT(cl2->doUpdate()) , cl2);
Output:
1cl->doUpdate()
1cl2->doUpdate()
Object::connect: No such slot client::cl->doUpdate() in .../main.cpp:14
Object::connect: No such slot client::cl2->doUpdate() in .../main.cpp:15
It will probably come down to the amount of introspection I can get into the signal/slot system.I'm not sure how to interpret the connect error message. It tells me that connect is concerned with the static information for the class Client, but the slot string indicates the specific instance name - I'm wondering if by the time I get to Model::connectHandle() this name loses its meaning.

It's a simple case of typo:
In class, you have doUpdate() slot.
In main func, you're passing onUpdate() to SLOT() macro.
Also, you shouldn't include the instance in the SLOT() macro, just the slot name (and parameters). Exactly the same syntax you'd use in connect(). Qt's signal-slot connection mechanism is based on string comparison. In other words, your main should do this:
foo.registerForUpdates(SLOT(doUpdate()), cl);
foo.registerForUpdates(SLOT(doUpdate()), cl2);

Related

Reverse QMetaMethod::fromSignal()

I already have a working connection from SomeClass::somSignal using the SIGNAL() macro but I don't have a way of connecting it to a lambda using &SomeClass::someSignal mostly because someClass is inaccessible. I have the QObject *, though.
I tried to look into QMetaObject; it should have some sort of look-up structure where these details are kept.
We can have:
QMetaMethod::fromSignal(PointerToMemberFunction)
returning QMetaMethod
My question is, is it possible to have something like this?
fromMetaMethod(QMetaMethod)
returning PointerToMemberFunction
??
Thanks.
One way to create the connection you want does not require you to find any methods address.Simply:
Create a signal-to-signal connection, from SomeClass::someSignal to your own signal in your own class using the SIGNAL() connection style.
Connect your signal to the lambda.
You only have to make sure the parameters of the original signal are forwarded to your lambda.
Example:
QObject::connect(
pointerToSomeClass, SIGNAL(someSignal(int)),
pointerToMyClass , SIGNAL(mySignal(int)),
Qt::DirectConnection
);
QObject::connect(
pointerToMyClass, &MyClass::mySignal,
[](int i) { ... }
);
If you happen to need the sender of the original signal, then you will need a slot.
Example:
void myClass::mySlot(int i)
{
emit mySignal(sender(), i);
}
and
QObject::connect(
pointerToSomeClass, SIGNAL(someSignal(int)),
pointerToMyClass , SIGNAL(mySlot(int)),
Qt::DirectConnection
);
QObject::connect(
pointerToMyClass, &MyClass::mySignal,
[](QObject* sender, int i) { ... }
);

Qt6 Connect Signal to Lambda Function

I'm using a DataRouter class to handle communication with a QSerialPort (and then communicate the results elsewhere). The connected device sends a status package every second or so, and I would like to read it without polling the device. I tried directly using QSerialPort's waitForReadyRead function, but no matter how long I set the wait time, it always timed out. Looking here and here I saw signals can be connected to Lambda functions. Now I'm trying to connect QSerialPort's readyRead signal to a Lambda which calls my on_dataRecieved function but I get the error C2665:"QObject::connect: none of the 3 overloads could convert all of the argument types. Below is an example of what I have:
DataRouter.h
template<class SerialPort>
class DataRouter
{
public:
DataRouter ();
private slots:
on_dataRecieved();
private:
shared_ptr<SerialPort> m_port;
};
DataRouter.cpp
template<class SerialPort>
DataRouter<SerialPort>::DataRouter()
{
m_port = std::make_shared<SerialPort>()
QObject::connect(m_port, &QSerialPort::readyRead, this, [=](){this->on_dataRecieved();})
}
template<class SerialPort>
void DataRouter<SerialPort>::on_dataRecieved()
{
//Do stuff
}
If your "target" is not QObject you need to use the following overload of connect. The problem is that, you are trying to use non-QObject as "context" to determine the lifetime of the connection and that's not possible. To mitigate it you will need to release the connection somehow on DataRouter's destruction; one way is to store what connect() will have returned and call disconnect on it later on.
As for the signal coming from a smart pointer, have you tried this:
connect(m_port->get(), &QSerialPort::readyRead, &DataRouter::on_dataRecieved);
Your m_port is not entity of QSerialPort class, that's why you don't have QSerialPort::readyRead that can be emitted from it. template<class SerialPort> doesn't do what you what, it is just name of templated parameter.
You probably wanted something like this:
DataRouter.h
class DataRouter : QObject
{
public:
DataRouter ();
private slots:
on_dataRecieved();
private:
QSerialPort* m_port;
};
DataRouter.cpp
DataRouter::DataRouter()
{
m_port = new QSerialPort(this);
connect(m_port, &QSerialPort::readyRead, this, &DataRouter::on_dataRecieved);
// or connect(m_port, &QSerialPort::readyRead, this, [this](){this->on_dataRecieved();});
}
void DataRouter::on_dataRecieved()
{
//Do stuff
}
You don't have to wrap Qt classes in smart pointers as long, as you provide parent class for them. Memory freed when parent is destructed.

Qt Get Notified by a signal emitted by a class member

I am trying to reuse a library as a module in Qt, therefore, I want be make less modifications/patches to it as possible. The goal is to be notified by the Q_EMIT that is emitted by a class member. My example code is as follows:
myclass.cpp
public:
MyClass::MyClass(QObject* parent) : QObject(parent)
{
this->manager = new QOfonoManager(this);
}
public slots:
void MyClass::manager_available()
{
qDebug() << "Manager available";
QStringList modems = this->manager->modems();
qDebug() << "Modems:" << modems << "-" ;
}
public:
void MyClass::test()
{
QStringList modems = this->manager->modems(); //Starts getting available modems
connect (this->manager,SIGNAL(availableChanged()),this,SLOT(manager_available()));
}
qofonomanager.cpp
void QOfonoManager::onGetModemsFinished(QDBusPendingCallWatcher* watcher)
{
......
Q_EMIT availableChanged(true);
}
QOfonoManager::QOfonoManager(QObject *parent) :
QObject(parent),
d_ptr(new Private)
{
.....
}
When I call MyClass::test, I expect the member class to signal availableChanged to MyClass::available_changed. What am I missing here, can I make use of the Q_EMIT availableChanged(true) without having to modify QOfonoManager code.
Any help is greately appreciated.
I think the underlying issue is that your connect statement doesn't match the signal spec for QOfonoManager::availableChanged. The signal is emitted with a bool parameter...
Q_EMIT availableChanged(true);
But your connect statement states it to be a signal with no parameters...
connect(this->manager, SIGNAL(availableChanged()), this, SLOT(manager_available()));
(Do you not get an error message at the console? Something along the lines of "QObject::connect: No such signal ..." ?)
Changing the connect statement to the following should help to fix the problem...
connect(this->manager, SIGNAL(availableChanged(bool)), this, SLOT(manager_available()));
Or, better still, use the new signal/slot syntax if you're using Qt5...
connect(this->manager, &QOfonoManager::availableChanged, this, &MyClass::manager_available);
In addition to the above you should heed the advice of #scopchanov & #ixSci regarding the placement of your connect statements. It's vital that connections are established before signals are emitted otherwise the notifications will be missed.

Call Qt object method from another std::thread

I have simple Qt form which represents main window of my app. It has method:
void gui_popup::on_pushButton_clicked()
{
QString text = ui->MainText->toPlainText();
text = "1\n" + text;
ui->MainText->setText(text);
}
Also I have some code, running in another thread, created like this:
std:thread* core_thread = new thread(&Init); //void Init()...
Then, at some moment or condition code from std::thread need to call gui_popup::on_pushButton_clicked(). I'm trying to do it like this:
void test_callback(void* object_ptr)
{
auto this_object = (gui_popup*)object_ptr;
this_object->on_pushButton_clicked();
}
In std::thread code I'm saving test_callback pointer and gui_popup object pointer. But when it starts calling on_pushButton_clicked() program halts with segmentation fault error. This code works fine with some other simple classes, but not with QtObject. What am I doing wrong?
UPDATE:
I've solved it this way:
void test_callback(void* object_ptr)
{
QMetaObject qtmo;
qtmo.invokeMethod((gui_popup*)object_ptr, "on_pushButton_clicked");
}
it is, of course, much more complex than using QThread, emitting signals and all other suggested solutions. However thank you everyone for trying to help.
I usually solve it like this:
class Foo : public QObject
{
Q_OBJECT
Foo()
{
// connect to own signal to own slot and hence "translate" it
connect(this, SIGNAL(some_signal(QString)),
this, SLOT(some_slot(QString)));
}
signals:
void some_signal(QString s);
protected slots:
void some_slot(QString s)
{
// do something with your gui
}
public:
void callback_proxy(std::string s)
{
emit some_signal(QString::fromUtf8(m_string.c_str()));
}
};
and then the tread does not need to know about QT:
void thread_run_function(Foo* foo)
{
foo->callback_proxy("Hello from Thread!");
}
As far as I understood this is save because the connect (signal,slot) does have a additional default parameter (Qt::ConnectionType type which defaults to Qt::AutoConnection). This tells QT to dispach signals into the qt main event loop if they originate from a foreign thread. Note that using this connection type essentialy makes qt decide on runtime whether to dispatch the signal or call the slot immediately.
HtH Martin
Edits: Some more info on default parameter and this link as reference:
See http://doc.qt.io/qt-5/qt.html#ConnectionType-enum

Pass pointer to itself in emit() in Qt

I have a class handling file transfers.
One of the emits is a finished() signal
on the parent side, I'd like to connect this finished() signal to a fileTransferFinished() Slot
But how can I know which instance is finished? as there are plenty of them running at the same time..
I know I can make use of the QObject::sender() method to return the emitter, but this way, I can't acces a method of my instance..
qDebug() << "finished " << QObject::sender()->getID();
it says no member named getID in QObject
I'd like to have my pointer inside the Slot function, is that possible?
Look at the prototype of Object::sender() in the documentation:
QObject * QObject::sender() const
It returns a pointer to QObject instance, hence you will not be able to call getID() method on that return value.
Cast the return value of Object::sender() to pointer of your class before using it :
YourClass * sender_obj = qobject_cast<YourClass*>(QObject::sender());
qDebug() << "finished " << sender_obj->getID();
Using sender() is not a clean solution. It breaks modularity which normally is the purpose of using signals and slots.
Documentation says:
Warning: This function violates the object-oriented principle of modularity.
It is recommended to use QSignalMapper.
In addition to previous solutions, and given you're using Qt 5, another option is to use a lambda that passes the object to your slot:
connect(object, &YourClass::finished, [this, object]() {
fileTransferFinished(object);
});