In previous post I explained basic usage of Shared Memory Map in Viz|Artist 3.x. This time we’ll take deeper look into it and I’ll show you how to construct simple communication model to share data and raise events on among different scenes on different machines.
Introduction to events
When you look at the bottom of scripting window in viz|artist, there’s a popup menu called events. It contains list of all events that can be handled in your script. Sometimes though it is necessary to define your own event – for example if we want to do something in response to action that took place in another scene or on another machine. When we look closely we will see that viz provides us with everything we need to achieve this.
Those of you who programmed a little probably know all the thing I’m going to talk about but to make this tutorial fully understandable for everyone I’m going to start with the basics. First of all lets define what exactly an event is? Event is like a notification that specific action has occured or has been triggered by the user. For example lets say a user has pressed mouse button or put a finger on a touchscreen. In this case one or more events are triggered depending on a scenario. First of all we will get MouseDown event and if a cursor has changed its position – MouseMove event also. In viz|artist we can handle those events by putting proper code inside OnLButtonDown and OnMouseMove subroutine’s body. That approach is exactly the same in every objective programming language.
When we take a look at the definitions of OnLButtonDown and OnMouseMove subroutines we can spot a small difference. The first sub has no parameters while OnMouseMove takes x and y coordinates of mouse cursor. That tells us that events can pass some parameters to handling subroutines, so we can differ data processing depending on their values, for example we can decide to react on MouseEvents only if they occur in specific region (0<x<100 and 10<y<200).
Defining event handling model
Once we know the basics lets proceed to the fun part :-). As you may gues we will base our event model on Shared Memory Map. It’s logical choice becase it provides us with mechanis we need for this task – sharing data. First thing we should do is we should create a scenario on how we want events to be triggered and handled. I’m going to show you simple approach to this problem which can be simply extended to fit all your needs.
Lets assume we have some script that will place data in two shared memory map variables. The first variable – lets call it action – will provide us with information what action has occured. The second variable – parameter – will contain some data related with this action. This script, lets call it event dispatcher, is going to be called every time one of our custom actions occurs. What about catching this event? Simple we will make use of predefined viz3 script event related with Shared Memory – OnSharedMemoryVariableChanged. As I mentioned in previous part of this tutorial – OnSharedMemoryVariableChanged occurs every time SharedMemory entry value changes. All we need to do is to register listener of certain SharedMemory entry and voila – we have what we wanted.
Implementation
Now, when we know what we what to construct, lets move on to actual scripting. First lets work on event dispatcher script. Here’s how I would do this.
Sub OnInit()
End Sub
Sub OnInitParameters()
RegisterParameterString("project_id", "Project ID:", "MyProject1", 30, 100, "")
RegisterParameterString("current_param", "Parameter:", "", 30, 100, "")
RegisterParameterString("current_action", "Action:", "", 30, 100, "")
RegisterPushButton("trigger", " Trigger ", 0)
End Sub
Sub OnExecAction(buttonId As Integer)
If (buttonId = 0) Then
Dim action_key As String = GetParameterString("project_id") & "_action"
Dim param_key As String = GetParameterString("project_id") & "_param"
VizCommunication.Map[param_key] = GetParameterString("current_param")
VizCommunication.Map[action_key] = ""
VizCommunication.Map[action_key] = GetParameterString("current_action")
End If
End Sub
Obviously this script doesn’t do much but I have constructed it in quite handy way, so it can be used both from within the scene and from external application. You may have noticed it has an extra parameter defined called project_id. Let me tell you why I defined it. Suppose we have a number of viz machines connected to the same Graphics Hub. We also have different scenes with our event handling routines inside on them. When a Shared Memory Map entry responsible for an action changes, all machines are notified. If they were operating on the same scene – thats fine. But if they were meant to work on different auditions – we have a problem. We should use unique names for entries operating in different projects, and that’s what I did. Now the entries containing our action and parameter are having unique names (MyProject1_action and MyProject1_param respectively). The rest part of the script just assings proper values to those entries. One thing that also need to be explained is why do we need to double assign action entry. The answer is pretty simple. The handling mechanism will always respond to the changes made on action entry. That’s why we must ensure OnSharedMemoryVariableChanged event occurs every time we want to. But if we want tell our handling script that action1 has occured and previously that script has handled action1 also – OnSharedMemoryVariableChanged event will not be triggered, because action entry hasn’t changed. But if we first clear this entry and then assign its value will can be sure everything will happen the way we expected.
Below I put sample code that triggers our events from whithin viz3 script.
Dim EventDispatcherContainer As Container
Sub OnInit()
EventDispatcherContainer = Scene.FindContainer("Scripts$EventDispatcher")
End Sub
Sub MyAction1()
EventDispatherContainer.ScriptPluginInstance.SetParameterString("current_param", "Action1 parameter")
EventDispatherContainer.ScriptPluginInstance.SetParameterString("current_action", "action1")
EventDispatherContainer.ScriptPluginInstance.PushButton("trigger")
End Sub
Sub MyAction2()
EventDispatherContainer.ScriptPluginInstance.SetParameterString("current_param", "Action2 parameter")
EventDispatherContainer.ScriptPluginInstance.SetParameterString("current_action", "action2")
EventDispatherContainer.ScriptPluginInstance.PushButton("trigger")
End Sub
As I mentioned our event dispatcher script can also be used from an external control application. We just need to send following command to viz|engine:
$(SceneName)*TREE*$Scripts$EventDispatcher*SCRIPT*INSTANCE*current_param SET Action1 parameter
$(SceneName)*TREE*$Scripts$EventDispatcher*SCRIPT*INSTANCE*current_action SET action1
$(SceneName)*TREE*$Scripts$EventDispatcher*SCRIPT*INSTANCE*trigger INVOKE
where $(SceneName) is the actual path to our scene in viz database. Of course we can also set those shared memory variables directly, but then we have to know what are the entries names and we need to clear action entry first.
Last thing we should to make our mechanism complete is the event handling script. Basically it will be placed inside OnSharedMemoryVariableChanged event’s body.
Sub OnSharedMemoryVariableChanged(map As SharedMemory, mapKey As String)
' we should listen for data on entries related to our project
Dim action_key As String = GetParameterString("project_id") & "_action"
Dim param_key As String = GetParameterString("project_id") & "_param"
' we should respond only if data has changed on action node
If (mapKey = action_key) Then
Dim action As String = VizCommunication.Map[action_key]
Dim param As String = VizCommunication.Map[param_key]
' if data length of our action is greater than 1 it means
' action event occured, otherwise it means only that
' action entry has been cleared
If (action.Length > 1) Then
If (action = "action1") Then
' handle action 1
ElseIf (action = "action2") Then
' handle action 2
End If
End If
End If
End Sub
That’s almost it. Last thing is to register changed callback of action entry. Here’s how I would do this:
' this is global variable - it containes the name of action entry
Dim CurrentEventActionKey As String
Sub OnInit()
' full name of event action entry
CurrentEventActionKey = GetParameterString("project_id") & "_action"
' unregister listener
VizCommunication.Map.UnregisterChangedCallback(CurrentEventActionKey)
' register a new listener
VizCommunication.Map.RegisterChangedCallback(CurrentEventActionKey)
End Sub
Sub OnInitParameters()
RegisterParameterString("project_id", "Project ID", "MyProject1", 30, 100, "")
End Sub
Sub OnParameterChanged(parameterName As String)
If (parameterName = "project_id") Then
' unregister previous listener
VizCommunication.Map.UnregisterChangedCallback(CurrentEventActionKey)
' get a new name of action entry
CurrentEventActionKey = GetParameterString("project_id") & "_action"
' register a new listener
VizCommunication.Map.RegisterChangedCallback(CurrentEventActionKey)
End If
End Sub
Conclusion
In this tutorial I showed you how can we simply create custom events mechanism. I explained and implemented both event dispatcher and event handler scripts. You can use this scripts in your scenes to create communication between scenes on same machine and also on different machines. All you have to do is place event dispatcher script somewhere in scene from which you want events to be triggered and event handling mechanism on scene which should wait for those events. The mechanism presented in this tutorial allows you to pass only one parameter with your event. You can easily extend this by defining more parameter entries or implementing event arguments object. Event arguments object can be defined using structure. Here are some examples of it:
' simple event arguments structure containing information
' about sender of event and additional parameter
Structure EventArgs
Sender As String
Value As String
End Structure
' event arguments structure containing information
' about sender of event and also starting and final
' position of its container
Structure PositionChangeEventArgs
Sender As String
StartPos As Vertex
EndPos As Vertex
End Structure
I hope you find this tutorial interesting and useful. If you have any questions about anything related to this article, please post a comment. I’ll try to answer to it as quickly as possible. Till next time.
GHTime Code(s): 62143 nc 7d177
Great post and easy to understand! Going to show this to lots of Viz users. Keep it up :)
This is really great thx alot, beside that, maybe you know a way how to read/write to externel files from within the script. There is a Fileselector Function but i have no idea how to read the selcted files. maybe you can help me with that. thx
It’s nice to hear you liked it. As for reading/writing files there are two methods in System object to do that. Respectively: System.LoadTextFile(path as String, result as String) as Boolean and
System.SaveTextFile(path as String, contents as String) as Boolean.
The first one reads text file contents into result variable and the second one saves contents string into your text file. For both of them you should specify valid path.
Hope this will help you get started.
Cheers
thank you very much and thank you for sharing your knowledge :)