Python
Contents
The IronPython Plugin "PY"
By leveraging the power of IronPython, the "PY" plugin allows us to run Python scripts in VoxCommando. Scripts can interact with VoxCommando, reading and setting variables, triggering events and executing other actions. This allows us to do many new things that VoxCommando was not able to do very well before such as:
- Math
- Complex logic
- Looping (for loop, while loop, etc)
- and pretty much anything else we can do with Python!
Remember, Python is case sensitive, and depends on proper indentation. Be careful not to mix spaces and tabs in your indentation. This is a common mistake with Python.
IronPython Is Not Quite Python
- IronPython is based on Python version 2.7. If you grab code that's written for Python 3.x, you might need to make some modifications to make it compatible.
- IronPython has advantages over regular Python in that we have direct access to various components of VoxCommando.
- IronPython also gives us access to many of the programming tools available in .Net. For example, in our Arduino Python scripts, we are using .Net's serialport class.
- Not everything that works correctly in standard Python will work in IronPython, but most things do.
Setting up the Python Libraries
You do not need to install Python to use this plugin. Everything that is required is provided with VoxCommando.
During installation, make sure the Python Libraries checkbox is selected. If you initially unchecked the Python Libraries box upon installing VC, simply run the installation file again, with the box selected.
To use the Python plugin, simply enable the plugin as you would any other plugin in VoxCommando.
Plugin Actions
Currently this plugin allows us to call the following actions from our VoxCommando commands.
PY.ExecString
Executes a single line of Python code. For example, we can use it to handle basic calculations within a VC command macro, or pass VC data to a function we've already defined and loaded as a Python script (using PY.ExecFile), etc. But if we want to do anything fancy, we should probably use PY.ExecFile.
PY.ExecFile
Executes all of the code in the file specified. You can use relative paths, and they will be interpreted relative to the main VoxCommando folder. It is generally a good idea to put all Python scripts into a folder such as ..\VoxCommando\PY.
If you need to pass parameters from VoxCommando to a Python function, you should define the function first using PY.ExecFile, then use PY.ExecString to call the function and pass in parameters. Search the forum for examples (here's one).
Functions only need to be defined once. You may wish to put all your Python functions into a single ExecFile that gets loaded when VoxCommando first starts up (i.e., triggered using a VC.Loaded event). Any function in this file can then be called within individual commands using the PY.ExecString action.
PY.*
List of all actions available in the LCB.
Accessing VoxCommando Methods from Python Code
Through the vc object, Python code can call the following methods available to all plugins:
- void triggerEvent(string strEventName, List<string> payloads);
- actionResult callAction(string strActionType, string strParams, List<string> payloads);
- void doWav(string strWavPath);
- Evaluates an audio file and tries to recognize speech
- object getObject(string strObject, string strSubObject);
- void log(string strLogText);
- Writes a line of text to the VoxCommando log file (if logging is enabled)
- bool savePayloadFile(string strPath, Dictionary<string, string> payloadPairs, bool subsetMatching);
- bool savePayloadListToXML(string strPath, List<xmlPayload> myxmlpayloads);
- probably too complicated for most users
- string getLastResult();
- returns the last result (a string) that was successfully returned by an action
- void setResultList(List<string> lstResults);
Triggering an Event
Event With No Payloads
For example, if you wanted to trigger an event in VC called "MyEvent.DoIt" you could use the following code:
vc.triggerEvent("MyEvent.DoIt",None)
Note: "None" is the Python equivalent of "Null" and we use it if we don't need payloads.
Event With Payloads
To include payloads in the event:
from System.Collections.Generic import * vc.triggerEvent("MyEvent.DoIt", List[str](["this is payload1","and this is payload2"]))
Actions
You can execute other VoxCommando actions by using vc.callAction, like this:
vc.callAction("OSD.ShowText","Display me for 5 seconds at the top&&5000&&-10", None)
As with vc.triggerEvent above, the final parameter is None because we are not passing any payloads. In other words:
We can make VoxCommando count to ten.
We can use Python to execute a loop. On each iteration of the loop, we call the "TTS.Speak" action.
for x in range(1, 11): vc.callAction("TTS.Speak","%s"%x, None) vc.callAction("TTS.Speak",". There, I counted to ten", None)
- "%s"%x is a Python method of formatting a string. In this case we are using it to convert an integer to a string
%s will be replaced by the value of x
- Note that in this case we are not using payloads, so we must send "None" which is the Python way of saying "null"
Generating Payload XML Using a Dictionary in Python
We can generate a payload XML file from a dictionary of key:value pairs.
from System.Collections.Generic import * myPayloads = {"payload value 1":"payload phrase 1","payload value 2":"payload phrase 2","payload value 3":"payload phrase 3"} path = "C:/VoxCommando/payloads/payloadtest.xml" vc.savePayloadFile(path,Dictionary[str,str](myPayloads),False)
CAUTION: The dictionary keys will become the *values* in your payload XML file. The dictionary values will become your payload phrases.
getObject
You can request data from VC by calling vc.getObject(string strObject, string strSubObject)
For example, to print the LastResult variable which was set by a previous action:
print vc.getObject("LastResult","")
strObject
Valid values for strObject are:
- "lastresult": returns a string
- "matches": returns a list of strings
- "displaylanguage": returns a string
- "payloads": returns a list of strings
- "fpayloads": returns a list of strings (in this case, friendly payloads)
- "evalvars" : returns a string where known VC variables in curly brackets have been replaced with their values
- available only in VoxCommando version 2.2.1.7 or later
#example for evalvars info = vc.getObject("evalvars","{LastSpoken}") print info
strSubObject
At the moment, no objects make use of the strSubObject parameter
Returning a Result
To send a result back to VoxCommando you need only set the "result" variable. The value will then be accessible to subsequent actions using {LastResult}
Examples
Example 1.
If we execute the following Python code:
result= 5.0*4/(3+4)
then {LastResult} will be equal to "2.85714285714286".
Example 2.
Verify Internet Status (forum thread)
Example 3.
For Loop (forum thread)
Python Scripting Forum Board
More use scenarios and explanations on issues such as:
- complex logic in python
- creating python functions
- passing information between VC and python
- comparing python solutions to VC-only solutions
can be found on the forum. In particular (though not exclusively), on the Python Scripting board.
Testing Code
- Open the "PY" plugin settings page to access a Python editor where you can test your code. Click "Execute" to see what happens.
- Another way to test code would be to edit a Python file in another program such as "Notepad++" and then test the code by calling the PY.ExecFile action from VoxCommando.
Notepad++ is a good way to edit your Python code and offers many advantages such as code highlighting.
Reinitializing the Python Engine
Sometimes you will need to clear out the various classes and methods that have been loaded into the Python engine. This can be done by:
- restarting VoxCommando, or,
- choosing "File >> Reinitialize Python" in the Python plugin window, or
- within a command macro, using the PY.ReInit action.
Killing Python Threads
- It is important to note that if you've used threading in your Python, these Python threads will continue to run in the background until they terminate themselves (or VoxCommando is closed).
- Choosing "File >> Reinitialize Python" will set a variable named "killPy" to true. You can use this to end threaded loops by writing your threaded loops as follows:
while not killPy: try: ...do your threaded loop stuff here ...do your threaded loop stuff here except: ...handle errors etc.