ReplicatePythonCallback

This page gives an example of using Python callbacks. It tries to keep the state of plots and operators in a two window configuration to be consistent. That is, if a plot or operator is added, deleted, or modified in either window, it attempts to make the same change in the other window.

Issues:

  1. By making a change in the other window, you are generating more actions, meaning your callbacks will get called again! This script has infrastructure for this.
  2. There appears to be a weird Python bug where global variables cannot be used with the comparison operator (==). That is coded around here.
  3. It only works for some of the RPCs and can probably be easily defeated if you try...

Here is the Python code:

# Script:   replicate.py
# Purpose:  This script will causing actions happening in each of two windows
#           to be mirrored to the other window.
#
# Note:     This script uses VisIt's Python Callback mechanism.  This mechanism
#           can be tricky to use, because actions done from within the script
#           can cause more callbacks to be executed.  For example, suppose the
#           user adds a plot in window 1.  This will call a callback in this script.
#           And the callback will add the same plot in window 2.  But this new
#           add plot call will be interpreted as a new event.  So the callback
#           will be called again.  But we really don't want it to be.  So we
#           detect this case and avoid the second invocation of the callback.
#
# Programmer: Hank Childs
# Creation:   May 6, 2008
#
#

import time

print "Starting replicate script..."

activeWindow = 1
numberOfSetActiveWindowsToIgnore = 0
numberToIgnore = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

def IssueMessage(s):
   ClientMethod("MessageBoxOk", str(s))

def SetOppositeWindowAsActive():
   global activeWindow
   # Python objects to using activeWindow directly when coming
   # from DeleteActivePlotsCallback.  Appears to be a Python bug
   t=int(str(activeWindow))
   if t == 1:
      SetActiveWindow(2)
   else:
      SetActiveWindow(1)
   global numberOfSetActiveWindowsToIgnore
   numberOfSetActiveWindowsToIgnore += 1

def RestoreActiveWindow():
   global activeWindow
   # Python objects to using activeWindow directly when coming
   # from DeleteActivePlotsCallback.  Appears to be a Python bug
   t=int(str(activeWindow))
   SetActiveWindow(t)
   global numberOfSetActiveWindowsToIgnore
   numberOfSetActiveWindowsToIgnore += 1

def MapCallbackToIndex(s):
   if s == "AddPlot":
       return 0
   elif s == "AddOperator":
       return 1
   elif s == "DrawPlots":
       return 2
   elif s == "SetOperatorOptions":
       return 9
   elif s == "SetPlotOptions":
       return 4
   elif s == "ChangeActivePlotsVar":
       return 5
   elif s == "RemoveLastOperator":
       return 6
   elif s == "RemoveAllOperators":
       return 7
   elif s == "RemoveOperator":
       return 8
   elif s == "DeleteActivePlots":
       return 3
   elif s == "SetActivePlots":
       return 10
   else:
       return -1

def StartCallback(s):
   idx = MapCallbackToIndex(s)
   global numberToIgnore
   if numberToIgnore[idx] > 0:
       numberToIgnore[idx] -= 1
       return 1
   else:
       numberToIgnore[idx] += 1
       SetOppositeWindowAsActive()
       return 0

def EndCallback(s):
   RestoreActiveWindow()

def AddPlotCallback(type, var):
   if StartCallback("AddPlot") != 0:
       return
   AddPlot(PlotPlugins()[type], var)
   EndCallback("AddPlot")

def AddOperatorCallback(type, fromDefault):
   if StartCallback("AddOperator") != 0:
       return
   AddOperator(OperatorPlugins()[type])
   EndCallback("AddOperator")

def DrawPlotsCallback(drawAllPlots):
   if StartCallback("DrawPlots") != 0:
       return
   DrawPlots()
   EndCallback("DrawPlots")

def SetPlotOptionsCallback(type):
   pl = GetPlotList()
   gotOne = 0
   nplots = pl.GetNumPlots()
   for i in range(nplots):
      plot = pl.GetPlots(i)
      if plot.activeFlag:
         atts = GetPlotOptions()
         gotOne = 1
   if StartCallback("SetOperatorOptions") != 0:
       return
   if gotOne == 1:
      SetPlotOptions(atts)
   else:
      print "Could not find the plot atts to set"
   EndCallback("SetOperatorOptions")

def SetOperatorOptionsCallback(type):
   # Note: probably won't work if there are multiple
   # operators of the same type in a plot.
   pl = GetPlotList()
   gotOne = 0
   nplots = pl.GetNumPlots()
   for i in range(nplots):
      plot = pl.GetPlots(i)
      if plot.activeFlag:
         noperators = len(plot.operators)
         for j in range(noperators):
             if plot.operators[j] == type:
                atts = GetOperatorOptions(j)
                gotOne = 1
   if StartCallback("SetOperatorOptions") != 0:
       return
   if gotOne == 1:
      SetOperatorOptions(atts)
   else:
      print "Could not find the operator atts to set"
   EndCallback("SetOperatorOptions")

def SetActivePlotsCallback(plots, activeOp):
   if StartCallback("SetActivePlots") != 0:
       return
   SetActivePlots(plots)
   EndCallback("SetActivePlots")

def RemoveLastOperatorCallback():
   if StartCallback("RemoveLastOperator") != 0:
       return
   RemoveLastOperator()
   EndCallback("RemoveLastOperator")

def RemoveOperatorCallback(idx):
   if StartCallback("RemoveOperator") != 0:
       return
   RemoveLastOperator(idx)
   EndCallback("RemoveOperator")

def RemoveAllOperatorsCallback():
   if StartCallback("RemoveAllOperators") != 0:
       return
   RemoveAllOperators()
   EndCallback("RemoveAllOperators")

def DeleteActivePlotsCallback():
   if StartCallback("DeleteActivePlots") != 0:
       return
   DeleteActivePlots()
   EndCallback("DeleteActivePlots")

def ChangeActivePlotsVarCallback(var):
   if StartCallback("ChangeActivePlotsVar") != 0:
       return
   pl = GetPlotList()
   nplots = pl.GetNumPlots()
   for i in range(nplots):
      plot = pl.GetPlots(i)
      if plot.activeFlag:
          ChangeActivePlotsVar(var)
   EndCallback("ChangeActivePlotsVar")

def SetActiveWindowCallback(id):
   global numberOfSetActiveWindowsToIgnore
   if numberOfSetActiveWindowsToIgnore > 0:
      numberOfSetActiveWindowsToIgnore -= 1
      return
   global activeWindow
   activeWindow = id

RegisterCallback("AddPlotRPC", AddPlotCallback)
RegisterCallback("DeleteActivePlotsRPC", DeleteActivePlotsCallback)
RegisterCallback("AddOperatorRPC", AddOperatorCallback)
RegisterCallback("DrawPlotsRPC", DrawPlotsCallback)
RegisterCallback("SetOperatorOptionsRPC", SetOperatorOptionsCallback)
RegisterCallback("SetPlotOptionsRPC", SetPlotOptionsCallback)
RegisterCallback("ChangeActivePlotsVarRPC", ChangeActivePlotsVarCallback)
RegisterCallback("RemoveAllOperatorsRPC", RemoveAllOperatorsCallback)
RegisterCallback("RemoveLastOperatorRPC", RemoveLastOperatorCallback)
RegisterCallback("RemoveOperatorRPC", RemoveOperatorCallback)
RegisterCallback("SetActivePlotsRPC", SetActivePlotsCallback)
RegisterCallback("SetActiveWindowRPC", SetActiveWindowCallback)

SetWindowLayout(2)
SetActiveWindow(1)
OpenGUI()
time.sleep(3)

IssueMessage("You are using the replicate script, which ensures "
    "that plots from two windows are kept in synch.  Before making "
    "plots, open a file, activate window 2, and then open another file.")