Qt Signal Slot Passing Array
This page describes the use of signals and slots in Qt for Python.The emphasis is on illustrating the use of so-called new-style signals and slots, although the traditional syntax is also given as a reference.
The main goal of this new-style is to provide a more Pythonic syntax to Python programmers.
- 2New syntax: Signal() and Slot()
Hi, I am working with an extra workerthread in Qt which communicates with the mainwindow via Signals and Slots. This works fine so far. Now I want to send a signal containing 2 arrays from my workerthread to the mainwindow, which doesn't really work.
Qt - Passing custom objects among threads. Details Category: Programming Written by Nandan Banerjee Hits: 13021 Communication between threads in a qt program is essentially done by using signals/slots. This is by far one of the most easiest and stable mode of communication amongst threads of a program. As for const QString &, I'm assuming that the slot implementation does not need to modify the QString s. If so, a pass-by-value (i.e. QString s), will make a needless copy of the QString object. On the other hand, passing the QString by reference (QString &) allows the compiler to pass a more efficient reference (in essence a pointer to the. In my most of legacy code. I am seeing passing parameters to signals as references Please find the example below. QObject::connect(sourceView, SIGNAL(signal(const QString&, const QString&, int)), ddestView, SLOT(slot(onst QString&, const QString&, int))); what is the significance of the same.
Traditional syntax: SIGNAL () and SLOT()
QtCore.SIGNAL() and QtCore.SLOT() macros allow Python to interface with Qt signal and slot delivery mechanisms.This is the old way of using signals and slots.
The example below uses the well known clicked signal from a QPushButton.The connect method has a non python-friendly syntax.It is necessary to inform the object, its signal (via macro) and a slot to be connected to.
New syntax: Signal() and Slot()
The new-style uses a different syntax to create and to connect signals and slots.The previous example could be rewritten as:
Using QtCore.Signal()
Signals can be defined using the QtCore.Signal() class.Python types and C types can be passed as parameters to it.If you need to overload it just pass the types as tuples or lists.
In addition to that, it can receive also a named argument name that defines the signal name.If nothing is passed as name then the new signal will have the same name as the variable that it is being assigned to.
The Examples section below has a collection of examples on the use of QtCore.Signal().
Note: Signals should be defined only within classes inheriting from QObject.This way the signal information is added to the class QMetaObject structure.
Using QtCore.Slot()
Slots are assigned and overloaded using the decorator QtCore.Slot().Again, to define a signature just pass the types like the QtCore.Signal() class.Unlike the Signal() class, to overload a function, you don't pass every variation as tuple or list.Instead, you have to define a new decorator for every different signature.The examples section below will make it clearer.
Another difference is about its keywords.Slot() accepts a name and a result.The result keyword defines the type that will be returned and can be a C or Python type.name behaves the same way as in Signal().If nothing is passed as name then the new slot will have the same name as the function that is being decorated.
Examples
The examples below illustrate how to define and connect signals and slots in PySide2.Both basic connections and more complex examples are given.
- Hello World example: the basic example, showing how to connect a signal to a slot without any parameters.
- Next, some arguments are added. This is a modified Hello World version. Some arguments are added to the slot and a new signal is created.
- Add some overloads. A small modification of the previous example, now with overloaded decorators.
- An example with slot overloads and more complicated signal connections and emissions (note that when passing arguments to a signal you use '[]'):
- An example of an object method emitting a signal:
- An example of a signal emitted from another QThread:
- Signals are runtime objects owned by instances, they are not class attributes:
(Originally posted on Sunday, February 19th, 2012.)
I've created over a dozen small projects using Qt by now. Most of the time Ithink I might as well make use of Qt's signals/slots system -- I mean it'salready there. And this almost always turns out to be a mistake, in terms ofprogramming effort, duplicated and fragile code, and what it is possible todo with the system.
Let me quickly summarize Qt signals/slots for completeness. Qt uses a codegenerator (the Meta-Object Compiler ormoc) to implement flexiblesignals/slots. Classes can mark themselves as moc'able with the Q_OBJECT
macro (and must inherit QObject
), then indicate that some functions in theclass are slots, and some are signals. Slots have declarations and definitionsjust like normal functions; signals are essentially just a function prototype,and have no definitions (the moc provides them).
Any signal can be connected to a slot with a matching signature, and indeed asignal can be connected to another signal. It's possible for the slot/secondsignal to have fewer arguments than the first. The signals are processed duringQt's event loop and, if the target is in another thread, the arguments areautomatically serialized and sent to that thread's event queue. This is verynice to have, as you can imagine.
The problem with connections
The issue I have with Qt signals and slots is how they are connected. Someclass must know about both the signal and the slot (or second signal) and callQObject::connect()
. For GUI widgets this works quite well: you connect thevalueChanged() signal of the spin box to the setEnabled() slot of the 'Apply'button, so that when the user changes a setting they can now apply theirchanges. Or you connect a signal to your own private slot, do a littleprocessing, and when you're done you emit a simplified signal of your own.Seems like a good system.
The catch is that some class must know about both the sender and thereceiver. Essentially this means signals/slots cannot go more than one levelup or down in your object hierarchy. So when you start using them in a non-GUIcontext, and you need to communicate information to a great-uncle (so to speak)or some other object far removed, you need to duplicate the signal at everyobject it passes through! (Fortunately you don't need slots, just signals, andthe top-level class can connect signals of two of its children togther.) Thisis a lot of duplicated information. I have had multiple signals which had to beduplicated at least two or three times in this way to get to where they neededto go.
This makes the code hard to change: you don't want to add a parameter to thesignal, or remove the signal, or add another one, because it will involvedigging through all these other classes and changing their signals andconnect()
calls. And heaven forbid you want to refactor some of thoseclasses, or move them around in the object hierarchy.
Alternative designs
Of course there are alternative designs one could employ: since signals/slotsare runtime-checked and not compile-time checked, you could pass aroundQObject
s that are interested in being registered for some event, or walkthrough a hierarchy and add connections in a separate pass. And I'm sure thereare many other ways. But I haven't found any that appeal to me yet.
The problem is simply that someone has to know about both the sender and thereceiver. It's quite easy to remove this requirement if you build your ownevent system. Just have a singleton EventSystem
class which can registerObserver
objects for various concrete subclasses of Event
. Anyone can grabthe singleton and register an observer object (or more likely a method, it'seasy enough to wrap a method in a functor object). Anyone can grab thesingleton and emit an event. There are no ties whatsoever between the senderand the receiver. They are completely disconnected.
This may sound hopelessly chaotic, but it's not, really (as long as you havea good way for observers to automatically get unregistered when theiroriginating object is destructed). I've occasionally needed to turn on tracingto see how many objects register themselves to listen for a particular eventtype, but such debugging is rare. Mostly you register a method, or emit anevent, and forget about it. The amount of code required is much less.
Aha, you say, but you've replaced Qt's nice object-based signals/slots systemwith a global set of events which is sure to become unmanagable! Well, Iusually have a singleton event 'hub' per namespace (like NetworkHub
orQtGUIHub
). I mark the events that are private to the namespace, and also theevents that are intended for a wider audience. Normally it works out to ahandful of events per dozen classes. If there are more, probably the couplingbetween areas of the system is too high.
(You could try to make Qt signals/slots global: one singleton, with signals.But as far as I could tell you'd have to duplicate every signal in the classthat was actually emitting it, because you can't emit another class's signals.And someone would have to connect all those signals together.)
To me, events should be global. Maybe this is my problem, that I try to treatQt signals/slots as events, and I should learn how to use them properly. Butthese are my conclusions so far.
What boost can do
I should add that writing your own event system takes a lot of work, especiallyif you want observer lifetime management, multithreading support, and so on. Itend to use boost instead, which can supply all this with very minimal amountsof code. Specifically, I use theBoost.Smart Ptr,Boost.Signals, andBoost.Bindlibraries (links for 1.48, use Google if they're broken). In an event hub youcan define an event like this (and this means an event class is not required):
Then you can add method observers and emit events with these macros:
(Yes, you need parameter-numbered versions of these macros. I'm working onthat. __VA_ARGS__
is C99... I'm thinking a typelist template where you canshift on args with <<
shows promise....)
Lifetime management of observers is performed automatically with sharedpointers: any class which has a method observer must inherit the classboost::signals::trackable
, which I usually typedef to something else.
If you want multithreading support, just add another ASYNC_EMIT_EVENT
macro which binds all the arguments to form a zero-arg functor, then registerthe event as an observer for a zero-arg signal. Another thread can comealong and 'fire the observers' for this signal (passing it no arguments), thuscalling all the queued up events with the arguments they were supposed to have.Then clear all the 'observers' unless you want duplicate events. Wrapeverything in sufficient mutexes (or use Boost.Signals2? that never worked forme) and you're good to go. Role-reversal: events are really observers inanother guise. Very sly, I know. Took me a while to come up with that one.
Also, you can use both Qt signals/slots and boost signals in the same project.Qt unfortunately uses #define signals protected
somewhere in its headers, and'signals' is the name of the boost library. But you can convinceboost::signals
to rename itself to work around Qt and then everything worksjust fine. (The interesting classes are put right into the boost::
namespaceanyway.) Seethe Boost.Signals FAQ.
Miscellaneous complaints
I have a few other gripes about Qt's signals/slots. First of all, if you havea slot which takes as an argument a class you've defined, and you want toconnect a signal from a different namespace to that slot -- well, the only wayI can usually get this to compile and run is to fully qualify the class in boththe signal and the slot (since Qt's string-matching argument checks willotherwise fail). And of course the same goes for all other signals/slotsconnected to these. There might be a better solution for this, but I think Qtdoesn't really use namespaces so they don't really notice issues like this.
There's no type conversion in Qt signals/slots. If you want to turn an int
into a double
, or an int
into a uint32
, you need to create a one-lineslot just to do so. With just about any C++ signals implementation (whether acustom-built one, or boost's), you get C++ type-conversion.
It's often really difficult to figure out the source of a signal. Sure,QSignalMapper
can bind objects to numbers or strings, but only for signalsthat originally had zero arguments! At least twice I've had to emit twoseparate signals to get across all the information I needed: e.g., a socketID (its index in a list of sockets), and the newly received data. The bestsolution, of course, was to not use so many signals/slots, have the socketstore its own index and use that.
This is the final complaint I have about signals/slots: they tend to encourage'bottom-up' programming. For example, a socket will receive some data and emita signal; its parent will catch that and figure out which socket ID, thenre-emit that; the general networking class will catch that and pass it on toa different child, the packet parser, and so on. I've used the word 'catch'deliberately here. It's like noticing a change in data at the lowest level andthen throwing exceptions all the time to report this back to the caller. Usingsignals/slots messes with the normal function calling conventions so badly youcan't always figure out what your own code is doing.
When you use an event hub class, you tend to only put important events in thereand not bother with small everyday communications. Which means that these smallcommunications will be ordinary function calls and hence much easier to follow.Object-message-passing might be fine for Objective C (I don't know thelanguage), but C++ should be written as C++ was intended to be written ....
I guess maybe that's the primary benefit for me of not using Qt signals/slots.I end up with fewer events. Maybe that's all I need.
Last words
Qt Signal Slot Passing Array Calculator
Finally, I must say that Qt's object lifetime management system has caused meat least as much grief as the signals/slots system. Again, it might be fine forwidgets, but for everything else, it's very hard to tell when your objects aredeleted. If ever. The number of bugs it's caused ... like a socket handshakerthat was supposed to have been deleted but was still slurping up all mynetwork traffic ... look, that's the subject of another rant almost as long asthis one, which I don't have time to write. But its conclusion is, always useboost::shared_ptr
instead of Qt's object lifetime management whenever you canget away with it. Then you know precisely which objects are keeping anotherobject alive, and you don't have to worry about a parent object. Just my(slightly) informed opinion so far.
Qt Signal Slot Passing Arrays
Don't get me wrong, I enjoy using Qt for GUI design. I think its layouts andHTML support and container-widgets make it a very powerful library for GUIs. Ijust let that convince me that Qt would therefore be good for everything else,too. But it isn't, necessarily. Just be aware of the other tools that are outthere and how they can be used. (And checking what Boost has is often a goodidea.)