Introduction

The BrainStim script engine supports the Qt-style signal and slot connections, see this online document. Signals and slots are used for communication between objects. This is an important feature for the script engine because it can make the script very flexible and powerful. It allows the script to dynamically connect/disconnect various objects to each other by connecting their signals and slots. These objects can then communicate to each other outside the main program loop.

Signals are emitted by an object when its internal state has changed in some way that might be interesting to another object. When a signal is emitted, the slots (or even signals) connected to it are usually executed immediately, just like a normal function call. When this happens, the signals and slots mechanism is totally independent of the main program loop.

Slots can be custom defined script functions or member functions from script objects. It is currently not possible to define a new signal in the script, this means that all signals must be defined in their underlying C++ object classes. The following example shows how Signal/slots connection can be used inside the script to pass a signal (an event) from one object to another slot (a script function). If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.

Example

Let's try to create a Signal-Slot connection and make use of it in the script

  1. Start BrainStim.
  2. Open the file SignalSlotMechanism.qs that is located in the Main Program Directory subfolder \Examples\BrainStim\QtScript SignalSlotMechanism\
  3. Examine the script
    We see that first a custom QDialog is created with two QPushbutton controls placed on a QGridLayout. Just before the custom dialog is shown some Signal/Slot connection are made using this syntax:
    <objectname>.<signalname>.connect(<slotname>);

    like:

    cClickButton.clicked.connect(onClickButtonClicked);

    This connects the signal clicked() from the QPushbutton cClickButton to a local defined function slot onClickButtonClicked().
    Then there's also a Signal/Slot connection made for the cExitButton QPushbutton handling.

    cExitButton.clicked.connect(onExitButtonClicked);

    After all connections are made the we see that the dialog is shown and the main script loop ends, no more processing to be done. If one of the two buttons is pressed then the corresponding connected function slot is executed. When the cExitButton is clicked then the CleanupScript() function slot is executed for the garbage collection so the script can exit.

    Important! Aside from connecting a clicked() signal to a function slot onExitButtonClicked() for handling the exit of the script, like described above here, there's also something else implemented in the example script for the handling of when a user closes the dialog manually (by clicking the x in the top right border of the dialog). This is done by overriding a virtual function from the cDialog (QDialog) named closeEvent(). Because this is a virtual function we can override it with our own custom implementation, we do that in the example script like:

    cDialog.closeEvent = function()
    {
        Log("cDialog closeEvent() detected!");
        CleanupScript();
    }

    By overriding this virtual function like this we can make sure that whenever the user manually closes our custom dialog the script is then finalized.

    Finally a function named CleanupScript() is used for the implementation of the correct garbage collection handling so the script can end successfully.
    Inside this CleanupScript() function we see that all Signal/Slot connections are again disconnected using the following syntax:

    <objectname>.<signalname>.disconnect(<slotname>);
  4. Execute (press the F5 key) the QtScript code and watch what happens when you click the buttons of the custom dialog, also try to close the dialog by clicking the x in the top right border of the dialog. Verify that everything works by watching the 'default' OutputLog tab messages while trying.