Can we do Python callbacks from the host application?

In our application, we would like Python code to pass a callback into the host application’s API which would then be called by the host application. I have been experimenting with ScriptRun.RunFunction() using the CallMethod quickstart demo but it appears that every call to the Python script is a new execution. We would like every callback to be into the same execution so that Python script state is preserved between calls. For example, here is my modified Python from the CallMethod quickstart:

arenaBackgroundBrush = SolidColorBrush(Colors.DarkBlue)
dotBrush = SolidColorBrush(Colors.White)

def DegreesToRadians(degrees):
  radians = (Math.PI / 180) * degrees 
  return radians

def UpdateBrush():
  newRed = dotBrush.Color.R - 30
  if newRed < 0:
    newRed = 255
  return SolidColorBrush(Color.FromArgb(255, newRed, 255, 255))
  
def OnRender(dc, bounds, pen, currentAngle):
  maxSide = Math.Max(bounds.Width, bounds.Height)
  radius = (maxSide - (maxSide / 3)) / 2
  dotRadius = maxSide / 20

  arenaBounds = bounds
  arenaBounds.Inflate(-2, -2)
  center = System.Windows.Point((arenaBounds.Left + arenaBounds.Right) / 2, (arenaBounds.Top + arenaBounds.Bottom) / 2)
  dc.DrawEllipse(arenaBackgroundBrush, pen, center, arenaBounds.Width / 2, arenaBounds.Height / 2)

  radians = DegreesToRadians(currentAngle)

  dotCenter = System.Windows.Point(center.X + (Math.Cos(radians) * radius), center.Y + (Math.Sin(radians) * radius))
  dotBrush = UpdateBrush()
  dc.DrawEllipse(dotBrush, Pen(), dotCenter, dotRadius * 2, dotRadius * 2)

UpdateBrush will shift the color of the global dotBrush with every call to OnRender. What I want to see is the color of the ellipse change as the animation runs. What I actually see is the same color all the time. That tells me the Python is being started over again every time ScriptRun.RunFunction is called. I expected that since the QuickStart keeps all state in the C# viewmodel.

Is there something I am missing that will let me do this? I have been looking at IronPython and Python.Net and I am not sure this is even possible but I hope I am missing something. Thanks!

Hi Nathan,

We reuse the last execution scope when re-running the function with the different arguments.
In the code that you’ve provided, it looks like dotBrush is local to the function OnRender.
In order to use a global variable, you need to prepend it with the global keyword:

Below is a modified code snippet. Please let me know if it works for you.

Regards,
Dmitry

arenaBackgroundBrush = SolidColorBrush(Colors.DarkBlue)
dotBrush = SolidColorBrush(Colors.White)

def DegreesToRadians(degrees):
  radians = (Math.PI / 180) * degrees 
  return radians

def UpdateBrush():
  newRed = dotBrush.Color.R - 30
  if newRed < 0:
    newRed = 255
  return SolidColorBrush(Color.FromArgb(255, newRed, 255, 255))
  
def OnRender(dc, bounds, pen, currentAngle):
  maxSide = Math.Max(bounds.Width, bounds.Height)
  radius = (maxSide - (maxSide / 3)) / 2
  dotRadius = maxSide / 20

  arenaBounds = bounds
  arenaBounds.Inflate(-2, -2)
  center = System.Windows.Point((arenaBounds.Left + arenaBounds.Right) / 2, (arenaBounds.Top + arenaBounds.Bottom) / 2)
  dc.DrawEllipse(arenaBackgroundBrush, pen, center, arenaBounds.Width / 2, arenaBounds.Height / 2)

  radians = DegreesToRadians(currentAngle)

  dotCenter = System.Windows.Point(center.X + (Math.Cos(radians) * radius), center.Y + (Math.Sin(radians) * radius))
  global dotBrush
  dotBrush = UpdateBrush()
  
  dc.DrawEllipse(dotBrush, Pen(), dotCenter, dotRadius * 2, dotRadius * 2)

Whups! Got it, thanks!

1 Like