VisIt's Python Interface (also called Command Line Interface - CLI) features the ability to call user-defined Python callback functions in response to various VisIt actions. If you have both VisIt's GUI and Python Interface running at the same time, each action that you take in the GUI is logged in the Python Interface. In addition to the logging that already occurs, you can install callback functions that you've written in Python for various VisIt actions. This means that when VisIt performs a specific action, your Python function will be called so you can perform additional processing.
- Note that you should not make any assumptions as to when your callback function will be called because callback function execution requests are queued up on another thread and executed from that thread, which is independent from the main Python thread. This mechanism is required so the callback functions do not interfere with the VisIt Python Interface's normal execution. This means that you cannot rely on any implied synchronization between the regular control flow of the main Python thread and the thread that executes callback functions.
These functions are defined in the VisIt Python Interface and they are used when creating user-defined callback functions in Python. VisIt provides a set of callback function "slots", which are entries in a table of named callback functions. If a slot has an associated callback function then that function gets called when the state object or action associated with the slot is updated by VisIt.
The RegisterCallback function is used to install user-defined callback functions for state objects and for actions. In any case, only one user-defined callback may be installed for any given state object or action.
RegisterCallback(callbackName, callbackFunction [, userData]) callbackName -> This is the name of the callback for which you're registering a callback function. To obtain a tuple of valid callback function names, call GetCallbackNames(). callbackFunction -> Your user-defined callback function. userData -> An optional argument that allows you to pass any Python data you want to your callback function. If you add user-defined data then your function will have to accept an additional argument at the end of its argument list to accommodate your user-defined data.
The RegisterCallback function is also used to uninstall a user-defined callback function or install a different user-defined callback function.
The GetCallbackNames function returns a list of the valid names with which you can associate a user-defined callback function. You need to use one of the names from the tuple returned by GetCallbackNames when you register a callback function.
names = GetCallbackNames() # Print some of the names... >>> print names[28:28+17] ('BoundaryAttributes', 'ContourAttributes', 'CurveAttributes', 'FilledBoundaryAttributes', 'HistogramAttributes', 'LabelAttributes', 'MeshAttributes', 'MoleculeAttributes', 'ParallelCoordinatesAttributes', 'PseudocolorAttributes', 'ScatterAttributes', 'SpreadsheetAttributes', 'StreamlineAttributes', 'SubsetAttributes', 'SurfaceAttributes', 'TensorAttributes', 'VolumeAttributes')
Note that since VisIt's plugins are loaded at runtime and users may have different plugins loaded, you should not assume that the tuple of callback function names will be the same for all users. The portions of the tuple where the names correspond to the state object names for plot and operator plugins can differ, depending on user settings or on the version of VisIt being used. For this reason, all callback function "slots" are identified by name.
The names in the tuple appear in the following order: global state objects, plot state objects, operator state objects, action names.
When you are developing a callback function for a specific action, the argument count for your function will vary based on the number of arguments provided to the original ViewerProxy method used to generate the action. You can determine the argument count for a specific callback function "slot" by calling the GetCallbackArgumentCount() function.
GetCallbackArgumentCount(callbackName) -> integer callbackName -> The name of the callback function slot for which you want the number of arguments that user-defined callback must accept.
Registering a callback function
To register a callback function, you must do two things:
- Write a function that you want to be called in response to an action
- Register that function with the RegisterCallback function
There are essentially two flavors of Python callback functions that you can create. The first is a state callback function, which VisIt will call every time a state object of a specific type arrives from the viewer. New state objects come from the viewer to inform VisIt clients of VisIt's state and they contain information related to global settings as well as finer-grain settings such as plot attributes, which depend on the selected plot.
The other type of Python callback function that you can create is an "action" callback, which VisIt calls in response to the user performing a specific action such as opening a database. These fine-grain actions are performed in the VisIt clients by mapping user button clicks, etc into method calls on the ViewerProxy object, which drives VisIt's viewer. When an action callback is called by VisIt, the arguments that were passed to the ViewerProxy method are passed to the Python callback, allowing the callback function to intercept all of the information that was used to tell VisIt to perform the action.
Installing a state callback function
A state callback function is called whenever VisIt sends new state of the specified type to the Python interface. New state is sent from the viewer for a variety of reasons but most often, the user directly changed the values in the updated state object. State callback functions typically have a single argument, which is a copy of the state object that caused the callback function to be called. If you registered the callback function with user data then there will be a second argument to your callback function.
# Print the SliceAttributes every time they come from the viewer. def printSliceAttributes(atts): print atts RegisterCallback('SliceAttributes', printSliceAttributes)
# Pass this user data to the callback function. User data can be any type of # Python object so you can pass objects, dictionaries, functions, etc. userData = "Hi there: " # Print the SliceAttributes every time they come from the viewer. def printSliceAttributes(atts, userData): print userData, atts RegisterCallback('SliceAttributes', printSliceAttributes, userData)
Installing an action callback function
An action callback function is called after VisIt has executed a specific action. The number of arguments for an action callback function varies based on the arguments to the action that caused the callback to be executed. Actions are initiated when the client calls methods on ViewerProxy's ViewerMethods object, which maps method calls into a format that can be transmitted to VisIt's viewer. The number of arguments for the various ViewerMethods methods can be obtained by calling GetCallbackArgumentCount() with the name of an action. The meaning of the arguments in the callback function argument list follows follows from the argument list of the methods in the ViewerMethods object. You can determine the arguments and their meanings by examining the documentation for the ViewerMethods class. The documentation is for the Java client but the argument lists are compatible with the arguments that are passed to your Python callback function. However, note that the Java interface sometimes overloads functions with different argument lists. If you notice multiple functions of the same name in the Java interface, use the number of arguments reported by GetCallbackArgumentCount() for your callback function. As before, you can provide user-defined data to your callback function, increasing the argument count by one.
# Call this function whenever we open a new database. It will add a new # plot when we open a new database. def onOpenDatabase(database, timeState, addDefaultPlots, forcedFileType): metadata = GetMetaData(database, timeState) if metadata.GetNumScalars() > 0: AddPlot("Pseudocolor", metadata.GetScalars(0).name) DrawPlots() RegisterCallback('OpenDatabaseRPC', onOpenDatabase)
# User data for our callback function to demonstrate that we can pass # arbitrary data to our callback functions. def print_func(arg): print arg # Call this function whenever we change time states. def onSetTimeSliderState(timeState, userData): userData(timeState) RegisterCallback("SetTimeSliderStateRPC", onSetTimeSliderState, print_func)