User Tools

Site Tools


Signals and Slots

While developing tools and/or utilities using Daz Script, when one object changes we often want another object to be notified of that change so that the other object can react accordingly. Generally speaking, we want objects to be able to communicate with each other when certain events occur. An example of this could be, when the user clicks a DzButton labeled “Browse…” in a dialog, convention suggests that perhaps a DzFileDialog would appear and allow the user to navigate to a file and select it.

Other toolkits achieve this type of interaction using a technique referred to as a callback. While traditional callbacks can be used to an extent in Daz Script 1), the Qt framework 2) provides an alternative and achieves this inter-object communication through its Signals and Slots mechanism.

Signals

A Signal is emitted by an object when its internal state has changed in some way that may be interesting to another object. You will notice that many of the objects provided by Daz Script have a dedicated 'Signals' section in their documentation. These are not traditional methods; on the C++ side (i.e., the SDK) only the object that defines a signal and its descendants can emit a signal; on the script side signals can cause a signal to be emitted (see Emitting Signals).

When a signal is emitted, the slots that are connected to it are executed immediately; as if a direct call to the function was made at the same point in the code. The signals and slots mechanism is completely independent of the GUI event loop when this occurs. Once all connected slots have returned, execution of the code that follows the signal being emitted will occur. If several slots are connected to one signal, the slots will be executed sequentially, in the order they were connected.

Slots

As far as Daz Script is concerned, a Slot is any member function provided by an object that can be called in response to a signal; i.e. all member functions with the exception of constructors and signals.

Connections

In order for a signal to activate a slot, the slot must be connected to the signal. In DAZ Script, this is accomplished by using one of the Global connect(…) functions.

  • Signals may have one or more slots connected to them.
  • Slots may be listening (connected) to more than one signal.

Signal to Function Connections

connect( function )

With this type of connection, the argument to connect() is the identifier for the function to connect to the signal. The argument can be a script function, as in the first example, or it can be a QObject slot, as in the second example:

Signal_Script_Function_Connection
function myScriptFunction() {
    // ...
}
// ...
someQObject.someSignal.connect( myScriptFunction );
Signal_QObject_Function_Connection
someQObject.someSignal.connect( someOtherQObject.someSlot );

When the argument is a QObject slot, the argument types of the signal and slot do not necessarily have to be compatible; the application will attempt to perform conversion of the signal arguments to match the argument types of the slot, if necessary.3)

When a script function is invoked in response to a signal, the this object will be the Global object.

Signal to Member Function Connections

connect( thisObject, function )

With this type of connection, the first argument to connect() is the object that will be bound to the this variable when the function specified using the second argument is invoked.

Signal_Member_Function_Connection
function someScriptFunction() {
    print( this.x );
}
 
var oSomeObject = { x: 123 };
 
someQObject.someSignal.connect( oSomeObject, someScriptFunction );

Signal to Named Member Function Connections

connect( thisObject, functionName )

With this type of connection, the first argument to connect() is the object that will be bound to the this variable when a function is invoked in response to the signal. The second argument is a String that specifies the name of a function to connect to the signal and refers to a member function of the Object passed as the first argument.

Signal_Member_Function_Connection
var oSomeObject = {
	x: 123,
	func: function() {
	    print( this.x );
	}
};
 
someQObject.someSignal.connect( oSomeObject, "func" );

Overloaded Signals and Slots

When a signal or slot is overloaded, the application will attempt to pick the appropriate overload based on the actual types of the arguments involved in the function invocation. For example, if an object has slots someOverloadedSlot(int) and someOverloadedSlot(QString), the following example will behave reasonably:

Overloaded_Slot_Call
someQObject.someOverloadedSlot( 10 );   // calls the int overload
someQObject.someOverloadedSlot( "10" ); // calls the QString overload

You can specify a particular overload by using array-style property access with the normalized signature of the C++ function as the property name:

Overloaded_Slot_Array-Style_Call
someQObject['someOverloadedSlot(int)']( "10" );   // calls the int overload; the argument is converted to an int
someQObject['someOverloadedSlot(QString)']( 10 ); // calls the QString overload; the argument is converted to a string

If the overloads have different number of arguments, the application will pick the overload with the argument count that best matches the actual number of arguments passed to the slot.

For overloaded signals, the application will throw an error if you try to connect to the signal by name; you have to refer to the signal with the full normalized signature of the particular overload you want to connect to.

Emitting Signals

To emit a signal from script, simply invoke the signal function and pass in the relevant arguments:

Emit_Signal
someQObject.someSignal( "hello world!" );

It is currently not possible to define a new signal in script; i.e., all signals must be defined by C++ classes.

Disconnecting

Signal-Slot connections can also be disconnected. This happens automatically when the object that emits the signal is destroyed. Similarly, if the object with the slot that receives the signal is destroyed, the connection is automatically broken. Signal-Slot connections can also be broken programatically by calling one of the global disconnect() functions; substitute connect in a connection statement with disconnect.

Error Handling

When connect() or disconnect() succeeds, the function will return undefined. If either function does not succeed a script exception will be thrown. You can obtain an error message from the resulting Error object as shown below.

Connection_Error_Handling
var oSomeObject = { x: 123 };
 
try {
    someQObject.someSignal.connect( oSomeObject, "functionThatDoesntExist" );
} catch (e) {
    print( e );
}

Additional Information

The descriptions provided on this page were adapted from relevant portions of the Qt documentation at:

1)
Given its ECMAScript roots.
2)
The application framework that Daz Studio is built on top of.