User Tools

Site Tools


Signals and Slots

While developing tools and/or utilities using Daz Script, when one object (the “sender”) changes we often want another object (the “receiver”) to be notified of that change so that the receiving 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.

Signal

A Signal is emitted by an object when its internal state has changed in some way that may be interesting to another object. Notice that many of the objects available to Daz Script have a dedicated 'Signals' section in their documentation.

Signals are not traditional methods. On the C++ side (i.e., the SDK), only the class that declares a signal and its descendants can “emit” (call) it, but in script causing a signal to “emit” can be accomplished by invoking the function on an instance of an object and passing in the value to emit.

Emit_Signal
sender.signal( "hello world!" );

NOTE: Signals cannot be defined in script - signals are defined by C++ classes.

Slot

As far as script is concerned, a Slot (signal handler) is any function that can be called in response to a signal - i.e. script functions and all script-accessible QObject (and derived) member functions with the exception of constructors.

Connections

In order for a signal to invoke a slot, a connection between the signal and slot must be established. A signal may have one or more slot connected to it. A slot may be listening (connected) to more than one signal.

When a signal is emitted, the slot(s) connected to it are executed immediately - as though 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 each connected slot has returned, execution of the code that follows the signal being emitted will occur. If more than one slot is connected to a signal, each slot will be executed sequentially, in the order that their connections were established.

Connecting

In script, establishing a connection is accomplished by using one of the following methods (in order of recommended use):

  • Global::connect(…)
    • Automatically cleaned up when the script goes out of scope
  • Function::scriptConnect(…)
    • Available since 4.14.1.31
    • Automatically cleaned up when the script goes out of scope
  • Function::connect(…)
    • Deprecated since 4.14.1.31
    • Requires a symmetric call to disconnect(…) or it can result in a memory leak of the receiver object (and the connection) in future versions - see Disconnecting

Signal to Function Connections

  • Global syntax: connect( sender, “signal”, slot )
  • Function syntax: sender.signal.scriptConnect( slot )
  • Function syntax: sender.signal.connect( slot ) - deprecated

With this type of connection:

  • slot is the identifier (not quoted) of the function to connect to
  • slot can be a member function of a QObject (receiver), as in the first and second examples
    • The argument types of signal and slot do not necessarily have to be compatible - the application will attempt to perform conversion of signal arguments to match the types of slot arguments.3)
  • slot can be a script function, as in the third example
    • this in the scope of the slot function will be the Global object
Signal_Global_QObjectSlotRef_Connection
connect( sender, "signal", QObject.slot );
Signal_Function_QObjectSlotRef_Connection
sender.signal.scriptConnect( QObject.slot );
Signal_Function_ScriptFuncRef_Connection
function scriptFunc() {
    // ...
}
// ...
sender.signal.scriptConnect( scriptFunc );

Signal to Member Function Connections

  • Global syntax: connect( sender, “signal”, thisObject, slot ) - since 4.15.0.18
  • Function syntax: sender.signal.scriptConnect( thisObject, slot )
  • Function syntax: sender.signal.connect( thisObject, slot ) - deprecated

With this type of connection:

  • thisObject is the object that will be bound to this in the scope of the slot function if slot is a Function
Signal_Global_MemberFuncRef_Connection
function scriptFunc() {
    print( this.x );
}
 
var oSomeObject = { x: 123 };
 
connect( sender, signal, oSomeObject, scriptFunc );
Signal_Function_MemberFuncRef_Connection
function scriptFunc() {
    print( this.x );
}
 
var oSomeObject = { x: 123 };
 
sender.signal.scriptConnect( oSomeObject, scriptFunc );

Signal to Named Member Function Connections

  • Global syntax: connect( sender, “signal”, receiver, “slot” )
  • Function syntax: sender.signal.scriptConnect( receiver, “slot” )
  • Function syntax: sender.signal.connect( receiver, “slot” ) - deprecated

With this type of connection:

  • receiver is the object that will be bound to this in the scope of the slot function
  • slot is a String consisting of the name of a member function on the receiver object
Signal_Global_NamedMemberFunc_Connection
var oSomeObject = {
	x: 123,
	func: function() {
	    print( this.x );
	}
};
 
connect( sender, "signal", oSomeObject, "func" );
Signal_Function_NamedMemberFunc_Connection
var oSomeObject = {
	x: 123,
	func: function() {
	    print( this.x );
	}
};
 
sender.signal.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.

NOTE: Due to changes in future versions of the Qt framework 4), this syntax may not be supported in future versions of the application and should therefore be avoided where possible.

Disconnecting

Signal-Slot connections can also be disconnected. This occurs 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 programmatically via:

  • Global::disconnect() functions
    • Substitute connect in a connection statement with disconnect
  • Function::scriptDisconnect() functions
    • Substitute scriptConnect in a connection statement with scriptDisconnect
  • Function::disconnect() functions - deprecated
    • 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. Obtaining an error message from the resulting Error object can be accomplished as shown below.

Connection_Error_Handling
var oSomeObject = { x: 123 };
 
try {
    sender.signal.connect( oSomeObject, "functionThatDoesNotExist" );
} 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) , 4)
The application framework that Daz Studio is built on top of.