I am working on a project in C++ with Qt in Visual Studio. There are multiple level of signal to signal connection.
By saying that, I mean for example
//ExampleClass1
connect(this, ExampleClass1::signalToBeConnected, obj2, ExampleClass2::signalToBeConnected);
//ExampleClass2
connect(this, ExampleClass2::signalToBeConnected, obj3, ExampleClass3::signalToBeConnected);
//ExampleClass3
connect(this, ExampleClass3::signalToBeConnected, obj3, ExampleClass4::slotEnd);
Just like this example, there are signal to signal connections until the last signal is connected to a slot. Although it is obvious that the triggering signal of "slotEnd" comes from "signalToBeConnected" of ExampleClass1, if exists, I want to be sure that which signals are emitted.
Related
problem
i'm currently putting FUSE together with qt5. there is no bridge between Qt and FUSE yet, both the FUSE main thread (which is spawning the other working FUSE threads) and the QCoreApplication are simply running side by side.
but i want to be able to send and receive data between a QObject based object and the pthread's Read(..) function shown in [0] using Qt's SIGNALS and SLOTS.
question
now i want to alter the Read(..) function from [0] to retrieve data using Qt's SIGNALS and SLOTS from a QObject based class. sending a signal from a pthread works but without an explicit QEventLoop i can't receive the reply. therefore i was looking at the code from [1] which is excellent in design but i didn't get it working yet.
pseudo code (taken from [1]):
QNetworkAccessManager qnam;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
/* reply has finished, use it */
looks interesting, all i would need is a QObject deriving class similar to the QNetworkReply which would handle the request.
when i was playing with that code i had the problem that my implementation of QNetworkReply wouldn't wait for loop.exec() to be running and then the finished() SIGNAL wouldn't be received by the loop.
but isn't there something easier than to spawn a QEventLoop?
NOTE: the QNetworkReply and QNetworkAccessManager in the example from [1] is spawned inside the pthread, i however, need to be able to communicate with the QCoreApplication's even queue using SIGNALS and SLOTS since the object with the data in it comes from a different QThread (in this either the QCoreApplication or a special QThread).
using a Qt::QueuedConnection
i've also found [2] and maybe:
connect(src, SIGNAL(signal-signature), dest, SLOT(slot-signature), Qt::QueuedConnection);
is all i want but i doubt that.
links
[0] https://github.com/qknight/qt-fuse-example/blob/4d92a74fad22fd559588e58be67f766174c7efb8/qt-fuse/examplefs.cc#L74
[1] http://qt-project.org/wiki/ThreadsEventsQObjects#7494f2b4836907fc1c09311e3a0305e6
[2] emit Qt signal from non Qt Thread or ouside Qt main event loop with at 4.5
What you're likely facing is that QNetworkAccessManager internally uses threads to process http requests. That's why it "doesn't wait" for you. There's a rather simple modification needed to fix it:
QNetworkAccessManager qnam;
QEventLoop loop;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
if (!reply->isFinished()) loop.exec();
Things to note when using QObjects in Multiple Threads
When the object that is the source of a signal lives (is instantiated in) a thread different than the thread of the object with slots, the connection will be of the QueuedConnection type automatically.
The real issue is: Each QObject has a thread affinity. The default affinity is the thread where the object was instantiated. You're not supposed to use such objects directly from other threads.
What you're likely doing is instantiating the sender and receiver objects in the same thread, but then emitting the signal from another thread. This is a source of potential errors error and leads to undefined behavior if the user of such an object is forcing a non-automatic direct connection.
Whenever you do emit object->signal(...), the following invariant should hold:
Q_ASSERT(QThread::currentThread() == object->thread());
Feel free to add those invariant checks in front of every emit() that you explicitly perform.
If the assertion fails, you need to use QObject::moveToThread to move the object to the thread where you want to fire its signals. You can get a QThread for a given pthread by calling QThread::currentThread() from the code that runs in the pthread. An instance of a QThread will be created automagically for you :)
Yes, you want the Qt::QueuedConnection method. But also ensure that you are using the multithreading Qt library. IIRC it is a build-time option.
See also: Qt documentation
I have this code:
QWidget *w = qobject_cast<QWidget *>(d->m_object);
w->setObjectName("test");
it's can not emit objectNameChanged signal, so I want to manual emit signal,
w->objectNameChanged("test",QWidget::QPrivateSignal)); but report error QWidget::QPrivateSignal is private). How can I emit QWidget::objectNameChanged() signals?
From the Documentation (5.7):
Note: This is a private signal. It can be used in signal connections
but cannot be emitted by the user.
Anyway, the signal should be emitted, when you change the name, there should be a mistake somewhere else.
I have some problems with Qt. I have a class with a signal who's parameters are strings, and a slot. I'm connecting the signal to the slot in the class constructor. Also, I'm creating a thread in the class constructor. The thread reads data from a server and updates the UI(emits the UpdateMe signal). This is how I connect the signal to the slot:
QObject::connect(this, SIGNAL(UpdateMe(string, string)), this, SLOT(ModifyUI(string, string)));
I have a QTreeWidget with some file names. When I rename a file I notify the server and the server notifies the other clients. When I connect a single client there is no problem, but when I connect more than one client a problem appears: when I notify the server from the second client(when I write into the socket) the following error appears:
QObject::connect: Cannot queue arguments of type 'QVector<int>'
I tried to register QVector with qRegisterMetaType but I also have a signal that is emited when I modify an QTreeWidgetItem(when I rename the item, for example) and I need to dissconnect this signal when I want to change the item's text. If I register QVector I can't dissconnect this signal and the signal is emited.
When you register the QVector, does your call look like this?
qRegisterMetaType<QVector<int> >("QVector<int>");
Once you make this call, you should be able to emit the QVector type over queued connections.
If I register QVector I can't dissconnect this signal and the signal is emited.
Registering a metatype shouldn't prevent you from disconnecting a signal. It just allows you to queue types that aren't already registered with the meta system.
Most of the time, errors which look like this seem to be a result of mixing up threads, and specifically with this one, in my (limited) experience, a result of attempting to manipulate GUI elements "held" in the GUI thread using commands run a worker QThread.
I say "held" because quite often you get a complaint/error/crash saying something like "QObject: Cannot create children for a parent that is in a different thread." (i.e. the GUI thread).
The solution: from a non-GUI QThread ALWAYS communicate with GUI elements using signals and slots.
I'm trying to use signals and slots to pass information to the GUI thread from another thread, as I can't modify a pixmap from any other thread. I'm encountering a runtime error:
Object::connect: No such signal QThread::image_change(std::string) in visualiser.cpp:33
Judging from this, though I may be wrong, it looks like the signal is being searched for in the wrong namespace, as it is actually defined in Visualiser::image_change().
My code is as follows:
Visualiser.cpp:
QFutureWatcher<void> watcher;
connect(watcher.thread(), SIGNAL(image_change(std::string)), QCoreApplication::instance()->thread(), SLOT(update_image(std::string)), Qt::QueuedConnection);
QFuture<void> update_thread = QtConcurrent::run(this, &Visualiser::update_state);
watcher.setFuture(update_thread);
...
emit(image_change(imageSrc));
...
void Visualiser::update_image(std::string src)
{
QImage image;
image.load(src.c_str());
ui->visualContainer->setPixmap(QPixmap::fromImage(image));
}
visualiser.h:
signals:
void image_change(std::string src);
public slots:
void update_image(std::string src);
Don't pass thread pointers into connect - pass pointers to the sender and receiver of the event (like this). Because you're giving it QThread pointers instead, Qt is looking for those signals and slots in QThread, where they don't exist. If you give it Visualizer pointers instead, Qt will look for those functions in Visualizer, where they really are, and everything will work.
Hope that helps!
The source and the target of the connection are the same object, this, so the connect call should be:
connect(this, SIGNAL(image_change(std::string)), this, SLOT(update_image(std::string)));
Since the signal will be emitted from another thread than the one the Visualizer has an affinity with (see QObject::moveToThread()), the connection with the slot will automatically be queued, and the slot will be executed by the correct thread.
But for queued connection to work, Qt has to store temporarily the parameter until it can actually call the slot, which is done by converting it to QVariant, storing it somewhere, and then reconverting it to the actual type when the receiving thread is ready to execute the slot.
So you need to register std::string to Qt's metatype system with Q_DECLARE_METATYPE or change the signal and slot parameter type to one that is already registered to (like QString or QByteArray).
I am unable to find a proper simulation for ItemClicked() SIGNAL for QTreeWidget.
Is there a way to simulate it so that ItemClicked Signal is generated ?
e.g: we can emit ItemClicked in a derived class of QTreeWidget but cannot (as a QT rule) outside of it.
You can't use the emit call for class A to emit class B's signals. But note that the documentation for signals and slots says:
"You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)"
So you can work around this by declaring a signal in class A of the same signature as the one you want class B to emit, and connecting the signals together:
connect(
myclass, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
treewidget, SIGNAL(itemClicked(QTreeWidgetItem*, int))
);
Then emit itemClicked from myclass. If I'm not mistaken, it will work for this case...and fire the treewidget's itemClicked signal for you.