Can someone explain in simple terms the "signals and slots" pattern?
Signals and slots are a way of decoupling a sender (the signal) and zero or more receivers (the slots). Let's say you a system which has events that you want to make available to any other part of the system interested in those events. Rather than hard-wiring the code that generates event to the code that wants to know about those events, you would use a signals and slots pattern.
When the sender signals an event (usually by calling the function associated with that event/signal) all the receivers for that event are automatically called. This allows you to connect and disconnect receivers as necessary during the lifetime of the program.
Since this question was tagged C++, here is a link to the Boost.Signals library which has a much more thorough explanation.
I'm assuming you're talking about QT's signals and slots.
It's very simple.
An instance of a class can fire a signal and another instance of perhaps another class can catch that signal in a slot. It's sort of like a function call only that the guy that calls the function doesn't need to know who wants to receive the call.
The best way to illustrate is with an example.
The class QPushButton has a signal QPushButton::clicked(). That signal is fired whenever the button is clicked. The push button doesn't need to know who's interested to know that a click occurred. it just fires the signal and whoever's interested can connect to it.
The QDialog in which the button is placed in is infact interested to know when the button was clicked. It has the slot MyDialog::buttonClicked(). On MyDialog c'tor you need to connect() the buttons click() signal to the dialog's buttonClicked() slot so that the slot will be called when the signal is fired.
A bunch of more advanced stuff:
- Arguments, a signal can have arguments and these arguments can optionally be passed to the slot as well.
- cross thread calls - If you're making a signal-slot connection that needs to be cross thread then QT will automatically buffer the signals and queue them to the right thread. This happens automatically for instance when a GUI thread needs to communicate to a working thread.
I think one can describe signals and slots best when you are looking at them as a possible implementation vehicle for the Observer Pattern or Publish/Subscriber Pattern. There is one signal
, for example buttonPressed(IdType)
on the Publisher Side. Whenever the button is pressed, all slots that are connected to that signal are called. Slots are on the Subscriber Side. A slot could for example be sendMail(IdType)
.
Along with the event "button pressed", the slot would know which button was pressed, since the id would have been handed over. IdType
represents the type of the data sent over the connection between the Publisher and the Subscriber. An operation possible for the Subscriber would be connect(signal, slot)
which could connect buttonPressed(IdType)
with sendMail(IdType)
, so that if the button is pressed, that particular slot is called.
The good thing about this is that the subscriber (the slot side) doesn't need to care about details of the signal. It just needs to connect. Thus, here we have a great deal of loose coupling. You can change the buttons implementation, but the interface for the slots would still be the same.
Look at Qt Signals/Slots or Boost Signals for more informations.
Imagine having a GUI in your application. Most of the time, control flow wouldn't be very linear, i.e. instead of having a clear sequence of actions you'd have a user that interacts with the GUI (like buttons, menus etc.).
This is essentially an event driven model which can be implemented quite nicely with the signals and slots pattern. signals are events which are generated by objects (think GUI components) and slots are the receivers of those events.
Here is an example: imagine you have a checkbox, represented as an object in your programming language. Multiple things can happen to that checkbox: it can be toggled, which in turn also means that it's either set or unset. Those are the signals which it can emit. We will name them checkboxToggled, checkboxSet and checkboxUnset. As you see, in this example, the checkbox will always emit the checkboxToggled signal when toggled, but also exactly one of the two other signals, depending on how the state changes.
Now imagine having some other objects, namely a label, which for the sake of this example always exists as an object but can "appear" and "disappear" and a system beep (also represented by an object), which can simply beep. Those are the slots those objects have. We will call them "messageAppear", "messageDisappear" and "beep".
Suppose you want the system beep to beep everytime the checkbox is toggled, and the label to appear or disappear depending on whether the user checked or cleared the checkbox.
You would thus connect the following signals to the following slots (signals on the left, slots on the right):
checkboxToggled -> beep
checkboxSet -> messageAppear
checkboxUnset -> messageDisappear
That's basically it.
signals and slots may also have arguments. For example, using a slider which sets a numerical value, you would like to send the changed value along with the emitted signal as soon as the user moved the slider: sliderChanged(int).
Of course, to actually do something useful you would write some own classes which would contain some own signals and slots. This is done quite easily and using these own signals and slots, you have a nice way to interact with the GUI or other parts of your code in an event driven manner.
Keep in mind that signals and slots are often symmetric in the sense that there may often be a signal corresponding to a slot. For example, a checkbox may emit a signal when toggled, but it may also contain a slot that toggles the checkbox itself. It would be easy to implement to separate checkboxes that are always set oppositely to each other.