Orca Script Tutorial Written By Storm Dragon with Lots of help Note: I originally meant to try and teach the basics of Python in this tutorial. However, trying to do everything here would make this document incredibly long. So instead, I will give a link to an excellent Python tutorial. This is where I got my start. The link is: http://www.sthurlow.com/python/ 1: Creating Scripts There are two ways to go about this. For scripts to add global functionality such as time and date, battery status, weather, etc creating a file, ~/.orca/orca-customizations.py, will do the trick. Open a terminal, and type the following: cd .orca gedit orca-customizations.py This will create the file if it doesn't exist already. The second way is to create your own custom script area. This is actually quite a bit easier than it sounds. To set up your custom script area, open a terminal if you haven't done so already and type the following commands. cd .orca mkdir orca-scripts cd orca-scripts touch __init__.py There, you've done it! Your very own custom script area. Go ahead and celibrate, we'll wait... Now, for an explination of what was just done. cd .orca changes you to the .orca directory. It begins with a . because it is a hidden folder, so if you are in your home directory you won't know it is there unless you have view hidden directories on. Any file or folder that starts with a . is hidden. Just for reference, this is different from starting something with ./ which runs or executes a program. mkdir orca-scripts creates a new directory called orca-scripts. Just remember, mkdir equals make directory. cd orca-scripts Can you guess what this does? That's right, takes you into the orca-scripts directory. (2 points!) touch __init__.py is a neat command. It creates a file with nothing in it called, you guessed it, __init__.py. It's much easier than firing up your favorite text editor and navigating through a bunch of folders just to save a blank file. So, now that you have your very own custom script area, you may be wondering why you need such a thing. When a new program is loaded or gains focus, let's say Firefox for example, Orca searches several places to find out how it is supposed to handle the new program. In general, Orca is going to look for a script module whose name matches the name of the application in use. If that's not found, it's going to look for a module whose name matches the name of the toolkit in use. If that's not found, then it falls back to default.py. This can be changed by adding to settings._scriptMappings by calling settings.setScriptMapping. You can change the Firefox behavior by creating your own custom firefox.py script in the orca-scripts folder. This presents a special case though. IN the case of Firefox, you will most likely want to keep all of the abilities it has already like skip to next/previous heading etc. So, in your custom script, you will need to import orca.scripts.toolkits.Gecko in your script. Ok, if I haven't lost you so far, it gets easier, trust me. IN the next section I will give your brain as well as mine a chance to quit smoking. Let's write a script to add time functionality to Orca. 2: What Time Is It? Most screen readers have a key combination that when pressed speaks the time. Orca, however, does not have this feature built in to it. No big deal though, we'll just roll our own. The following script is an adaptation from the script found at: http://live.gnome.org/Orca/FrequentlyAskedQuestions#head-6a8c1c2511ba01d7397f68f754eec0d923d166f1 to get started, lets go to the .orca directory. To get to your .orca directory type: cd ~/.orca gedit orca-customizations.py This will open up a new file in Gedit. You can use your text editor of choice instead of course. Enter the following lines in as they are shown. Remember that indentation is of upmost importants in Python. So, if two spaces appear at the beginning of the line here, they must also be at the beginning of the line in your script. """This script adds time functionality to Orca Adapted by Storm Dragon from the script posted at: http://live.gnome.org/Orca/FrequentlyAskedQuestions#head-6a8c1c2511ba01d7397f68f754eec0d923d166f1 feel free to modify and/or redistribute this script as you see fit.""" import orca.input_event # watches for input that Orca recognizes import orca.keybindings # Handles binding keystrokes for Orca to use. import orca.orca # Imports the main screen reader import orca.speech # Handles Orca's speaking abilities import orca.braille # Displays information in Braille format myKeyBindings = orca.keybindings.KeyBindings() #Define the sayTime function def sayTime(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%I:%M%p", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) return True #end sayTime function sayTimeHandler = orca.input_event.InputEventHandler( sayTime, "Speaks and/or Brailles the time.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "t", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayTimeHandler)) # Sets Orca-t as the say time key orca.settings.keyBindingsMap["default"] = myKeyBindings #end time code >From the code above you can see that to work with speech you must import the speech library and to work with Braille, you must import the Braille library. The sayTime function is pretty straight forward. You first set the information you want to display in the message variable. Then, call the speech and Braille functions with the message you want spoken and/or Brailled. Be sure to remember to import the time library in this case or it won't work as expected. Some info about the time library. The line in the script that gives us our time message is: message = time.strftime("%I:%M%p", time.localtime()) For our purposes, only the part inside the quote marks matters. The %I gives the hour in twelve hour format. The : appears as a normal :, nothing special about it. The %M displays the minutes, and because it is in twelve hour format, %p will let us know if it's evening or morning with an uppercase PM or AM. For reference, here are the choices available when setting up a time and/or date string. Python Time Codes: %a abbreviated weekday name. %A full weekday name. %b abbreviated month name. %B full month name. %d Date of the month (01 to 31) %H Hour in 24-hour format (00 to 23) %I Hour in 12-hour format (01,12) %m Month number (01 to 12) %M Minute (00 to 59) %p AM or PM. %S Second (00 to 59) %y 2 digit year (00 to 99) %Y 4 digit year. %Z Time zone name. For more information about Python's time library: http://www.python.org/doc/2.5.2/lib/module-time.html The lines after th function add the key press to the learn mode. This is always a good thing to do for completeness and for those trying to learn their way around. Next comes the line that actually maps the key press. In our case the key is Orca-t. That is to say, insert-t for desktop layout or capslock-t for laptop layout. Simply put, the last line of code: orca.settings.keyBindingsMap["default"] = myKeyBindings ceals the bargin. Next, we will add more functionallity to our orca-customizations.py file and in so doing show that the more things change, the more they stay the same. 3: Adding Date to the Mix Why have date and time as two seperate commands? Well, I guess it's personal preference. When I want the time I don't particularly care what the date is, and usually when I want the date I don't want the time tossed in to it. Pluss, they each feel more important if they get their own special attention from you. Actually, I think short messages are best and I am willing to put up with remembering more key presses to keep them short. IF you want the time and date in the same command, simply change the date string in the code in the previous section. The thing that stands out about this next script, is it is exactly the same as the first script. It has tow functions instead of one, the second fuction is the same as the first the only thing changed is the time string. This time, it shows the date instead. There are 2 more lines added to add the key binding to learn mode and set the actual keys to press, in our case Orca-d. Here is the script: """This script adds time and date functionality to Orca New in this script, time or date can be pasted from the clipboard Adapted by Storm Dragon from the script posted at: http://live.gnome.org/Orca/FrequentlyAskedQuestions#head-6a8c1c2511ba01d7397f68f754eec0d923d166f1 feel free to modify and/or redistribute this script as you see fit.""" import orca.input_event # watches for input that Orca recognizes import orca.keybindings # Handles binding keystrokes for Orca to use. import orca.orca # Imports the main screen reader import orca.speech # Handles Orca's speaking abilities import orca.braille # Displays information in Braille format myKeyBindings = orca.keybindings.KeyBindings() #Define the sayTime function def sayTime(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%I:%M%p", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) return True #end sayTime function #Define the sayDate function def sayDate(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%A, %B %d, %Y", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) return True #end sayDate function #Set up sayTime keys sayTimeHandler = orca.input_event.InputEventHandler( sayTime, "Presents the time.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "t", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayTimeHandler)) # Sets Orca-t as the say time key #add sayDate info sayDateHandler = orca.input_event.InputEventHandler( sayDate, "Presents the date.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "d", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayDateHandler)) # Sets Orca-d as the say date key orca.settings.keyBindingsMap["default"] = myKeyBindings #end time and date code As promised, this code is almost exactly the same as the one in the last section. If you go back and compare the two files you will notice that they both have exactly the same code with the only additions being the sayDate function and the extra key binding commands. 4: Extra Functionality Now that you have the code to add speach and/or Braille as well as that necessary to add key bindings, all that is left is adding the scripts you want. Basically, more functions and imports. As a good example of what is possible let's build on our existing customizations.py. So far we can speak and Braille the current date and time. Perhaps, however, there are times when you would like to be able to add this information to a document. You can, of course, just type it in. What's the fun in that though? Also, the rules of laziness dictate that a quick key press should be used in this matter. So, let's take our time and date script and add the information spoken or Brailled to the clipboard. This way, once we have pressed orca-t for the time or orca-d for the date, we can press control-v to paste the provided information from the clipboard. Here's the code. """This script adds time and date functionality to Orca New in this script, time or date can be pasted from the clipboard Adapted by Storm Dragon from the script posted at: http://live.gnome.org/Orca/FrequentlyAskedQuestions#head-6a8c1c2511ba01d7397f68f754eec0d923d166f1 feel free to modify and/or redistribute this script as you see fit.""" #import gets code that has already been written. #think of it as using wheels instead of trying to reinvent them. import orca.input_event # watches for input that Orca recognizes import orca.keybindings # Handles binding keystrokes for Orca to use. import orca.orca # Imports the main screen reader import orca.speech # Handles Orca's speaking abilities import orca.braille # Displays information in Braille format #places text in the clipboard def setClipboardText(text): import gtk # import the gtk library cb = gtk.Clipboard() cb.set_text(text) cb.store() myKeyBindings = orca.keybindings.KeyBindings() #Define the sayTime function def sayTime(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%I:%M%p", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) setClipboardText(message) return True #end sayTime function #Define the sayDate function def sayDate(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%A, %B %d, %Y", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) setClipboardText(message) return True #end sayDate function #Set up sayTime keys sayTimeHandler = orca.input_event.InputEventHandler( sayTime, "Presents the time.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "t", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayTimeHandler)) # Sets Orca-t as the say time key #add sayDate info sayDateHandler = orca.input_event.InputEventHandler( sayDate, "Presents the date.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "d", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayDateHandler)) # Sets Orca-d as the say date key orca.settings.keyBindingsMap["default"] = myKeyBindings #end time and date code Although new functionality was added, the ability to paste from the clipboard, the basic script is the exact same. We had to import the gtk library, but this was only for the setClipboardText function. Originally I had the import gtk statement at the end of all of the Orca imports. I got to thinking that the library was only necessary for the clipboard though so I put it in the setClipboardText function. This serves two purposes. First, it shows us that it's not necessary for the whole Orca script, only for the clipboard function. Second, functions are locally scoped. This means that things inside a function are only there while the function is in use. So, and I may be wrong about this, because we only import gtk when it is necessary, it should speed up the script by a few mili seconds. It doesn't sound like much, but every bit helps. 5: Adding Weather IN this section I will show you how to get weather. The key binding is orca-w. This will present the current temperature | current conditions. For example 64 | mostly cloudy I really wanted to get this information from the top panel (where the clock is) but I couldn't find any information on accessing it. So, I did the next best thing. The weather info comes from Yahoo. This was a tricky piece of code, but after studying the developer documentation and writing and rewriting, I got it working. Here is the new script with documentation: """This script adds time and date functionality to Orca New in this script, time or date can be pasted from the clipboard Adapted by Storm Dragon from the script posted at: http://live.gnome.org/Orca/FrequentlyAskedQuestions#head-6a8c1c2511ba01d7397f68f754eec0d923d166f1 feel free to modify and/or redistribute this script as you see fit.""" import orca.input_event # watches for input that Orca recognizes import orca.keybindings # Handles binding keystrokes for Orca to use. import orca.orca # Imports the main screen reader import orca.speech # Handles Orca's speaking abilities import orca.braille # Displays information in Braille format #change the next line to your zip code: zipCode = 28624 #places text in the clipboard def setClipboardText(text): import gtk # import the gtk library cb = gtk.Clipboard() cb.set_text(text) cb.store() #getWeather function gets weather from Yahoo def getWeather(zip_code): if zip_code != 0: import urllib from xml.dom import minidom WEATHER_URL = 'http://xml.weather.yahoo.com/forecastrss?p=%s' WEATHER_NS = 'http://xml.weather.yahoo.com/ns/rss/1.0' url = WEATHER_URL % zip_code dom = minidom.parse(urllib.urlopen(url)) ycondition = dom.getElementsByTagNameNS(WEATHER_NS, 'condition')[0] weatherReport = ycondition.getAttribute('temp') + ' | ' + ycondition.getAttribute('text') else: weatherReport = "No zip code set: Please edit .orca/orca-customizations.py" return weatherReport myKeyBindings = orca.keybindings.KeyBindings() #Define the sayTime function def sayTime(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%I:%M%p", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) setClipboardText(message) return True #end sayTime function #Define the sayDate function def sayDate(script, inputEvent=None): import time # imports the Python time library message = time.strftime("%A, %B %d, %Y", time.localtime()) orca.speech.speak(message) orca.braille.displayMessage(message) setClipboardText(message) return True #end sayDate function #Define the sayWeather function def sayWeather(script, inputEvent=None): message = getWeather(zipCode) orca.speech.speak(message) orca.braille.displayMessage(message) return True #end sayWeather function #Set up sayTime keys sayTimeHandler = orca.input_event.InputEventHandler( sayTime, "Presents the time.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "t", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayTimeHandler)) # Sets Orca-t as the say time key #add sayDate info sayDateHandler = orca.input_event.InputEventHandler( sayDate, "Presents the date.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "d", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayDateHandler)) # Sets Orca-d as the say date key #add sayWeather info sayWeatherHandler = orca.input_event.InputEventHandler( sayWeather, "Get current temperature and conditions.") # Shows the function of the key press in learn mode myKeyBindings.add(orca.keybindings.KeyBinding( "w", 1 << orca.settings.MODIFIER_ORCA, 1 << orca.settings.MODIFIER_ORCA, sayWeatherHandler)) # Sets Orca-d as the say date key orca.settings.keyBindingsMap["default"] = myKeyBindings #end time, date, and weather code Once again, the code is basically the same. The only new additions are the sayWeather function which should seem awfully familiar, and the getWeather function which was quite interesting to write. This script is getting kind of long wouldn't you say? So, in the next section, let's tidy things up a bit. 6: Tidying UP As you have probably noticed, this orca-customizations.py file can get rather long in a hurry. Never fear though, import to the rescue. It is possible to write your scripts in seperate files and use import to import them into your orca-customizations.py script. So, for example, if you took all of the code for the weather and placed it into a file called weather.py in your .orca directory, you would then use: import weather This, in theory, will import the weather code from weather.py and because it is now in your orca-customizations.py file, it will work as expected. You can do pretty much anything you want with your Orca customiztions file. The only things you really are required to have are the keybinding. As long as you follow the pattern above, everything should be ok Attached is the customizations.py file created in this tutorial with the added functionality of battery status reporting. I hope you have found this helpful. Happy coding. Storm