The window eyes scripting manual has several good explanations of particular scripting issues; and a good list of resources to help you learn about various aspects of programming which may help you write a script. There are also at least three scripts I know of written specifically for the purpose of helping beginners advance their scripting knowledge.
What I don't know of is a comprehensive document, which attempts to make a start at explaining the various topics you'll need to understand in order to write a script; and perhaps help take you from a scripting igneramous to a scripting expert over time.
You have to start somewhere, so I'll start by saying that in order to write a script, you've got to know how to program in some computer programming language. A lot of the documentation in the scripting manual, and the wiki articles here, try to be helpful regardless of which language you're using, but they all assume a working knowledge of VBScript. So, you're first step should be to learn how to program if you don't already know how to; and since that usually involves learning a programming language, go out and try to find a programming tutorial book which uses VBScript. Here is a simple VBScript tutorial. If you can't find that, try for a programming tutorial book which uses the older version of visual basic (version 6 or earlier), before VB became .net, because it resembles VBScript more, and is much less complex to learn than VB.net. Failing those, you need to learn "object oriented" programming, in whatever language is available; then, when you feel comfortible as a "programmer", go and learn the VBScript language itself. Try here for a VBScript tutorial if you already know how to program. and try here for a very simple explanation of properties, methods, and objects, which are the heart of object oriented programming. These links, along with many others, can be found on the GW Micro Scripting Developer Resources page.
This raises the point, that's important to grasp, that learning how to program is a separate skill from learning any particular programming language.
This article, or series of articles, isn't going to teach you how to program if you've never programmed. you may want to know that in my opinion, learning how to program is a much bigger task than taking that knowledge, and proceeding to learn how to write window eyes scripts; so, if you don't know how to program, go learn in any way that you can, and then try and pick up an understanding of VBScript, because the rest of these articles will likely assume you'll be able to follow VBScript examples.
The differences between programming and writing a Window Eyes script
So, in general you know how to program, and you don't see how writing a script is anything more than the programming you've already learned? That's a fair question, and we'll try to answer it, but for now take it from a professional programmer: there's a great deal more knowledge you need to learn, after you've learned how to program, before you can consider yourself a good scripter; going through all that knowledge, one piece at a time, is the aim of the rest of these articles. if you'd like an analogy: it's something like learning a new programming language which is quite different from the ones you learned to program with.
The first point you'll need to examine is "what do scripts do"? "Why do we need them, what are they good for"? One thing they have been used for, but they weren't designed for and so perhaps should not be used for, is general purpose programming. If youwant a program to calculate the interest payments on your home mortgage loan, you don't need the WE scripting environment to do this. MS Windows provides you a way to write scripts, or general purpose programs, quickly and easily, and it's called the Windows Script Host; it will run VBScript programs for instance, and you don't need to learn anything about the WE scripting environment. So, we're back to why do you need to learn all the extra knowledge I've been refering to as the "WE scripting environment"? Here's a list, in no particular order, of answers to that question:
- they can improve the accessibility (to WE only of course) of a particular software application. If there's a particular application which can't be made to work well with WE, it's quite possible you can change that significantly with a script (referred to as an "application script"). Even if it works well enough with WE, you can add "convenience features" to it with a script, so that it's faster or easier to use it.
- You can also create similar "convenience features" with a script, which are available in all applications (referred to as a "global script"). An example might be a script which makes it particularly easy to spell the word under the cursor, or spell it phonetically.
- Sometimes these convenience features may have nothing to do with accessibility or WE, in which case they may look like "general purpose programming" (e.g. looking up a word under the cursor on the internet); however, the WE scripting environment makes writing this kind of program much easier than would a general purpose programming language, and so a WE script may be used simply for the convenience of the developer. Another important aspect of the "convenience of the developer" is the ability to design visual interfaces, such as a dialog which contains windows controls (listboxes, editboxes, radio buttons, etc.), without sighted assistance, and have this interface appear aestheticly pleasing and have it function well. This type of programming is particularly difficult for blind programmers (often requiring the use of a purely visual dialog layout aplication, which is not accessible), in almost every programming environment. However, WE scripting has added "xml-based dialogs" which allow blind programmers to do such programming, and so developing a program with a user interface similar to the Windows standard model is made much easier.
Capabilities of WE scripts
One reason to write a WE script instead of a more usual type of program is to make it easier to develope the program, by making use of some unique window eyes capability. One such capability has already been mentioned: the XML dialog facility. WE scripts have many other unique abilities to work with; all of these together are known as the "window eyes object model".
This object model is documented in the WE scripting manual, but only by listing every possible object, property, or method in alphabetical order. A beginning scripter would therefore benefit from a higher-level overview, introducing capabilities in some logical order (such as simple to advanced), and grouped by similar functionality. All of these capabilities are usable in your programming language of choice, and they are made possible by Window Eyes making available an enormous library of objects, properties, and methods, via the Windows COM facility. How to use objects, properties, and methods are a topic for the learning of object oriented programming.
WE also has two types of scripts, based on when the script first starts running, and when it stops. These two types of scripts are explicitly differentiated in the script manager. You can either have a script start to run when window eyes starts, and optionally stay running as long as WE is running (referred to as a "global script"), or, have WE start a script when a particular program starts, and stop the script when the program closes down (referred to as an "application script"). All of these scripts are running concurrently, and independant of each other. One of your first decisions, when designing a script, will be whether it should run all the time (a global script), or only when a given application is running (an application script).
All of the script types listed above can draw on the WE object model in order to achieve their tasks, the differences in the types only concern what programming language is used, and when the script is run. Therefore, it is necessary that a scripter become familiar with the capabilities of the WE object model. Below are just some of the capabilities of the object model that scripts may make use of, listed in broad categories:
- change any of the configuration settings within the WE control panel
- control the speech (it's voice, rate, other parameters), and the braille display
- define a hotkey, which can cause something to happen in your script (something spoken, something displayed, the window eyes environment is changed, etc.)
- examine what text is in any window; watch for specific text to appear, or to change, and take some action (such as speaking it) through the WE speech
- take some action when the user tabs to a new field, or perhaps when he presses a particular key (such as the arrow keys)
- cause window eyes not to speak certain text it would otherwise speak, or to speak it differently
- activate a particular window, or cause a particular control to have the focus
- send keystrokes into a specific program, as if they were typed on the keyboard
- open a dialog to get data from the user, and then process this data in some way
Finally, Starting to Write Some Code!
You should, by the time you reach this point, have an idea of what in particular you want a script to do, given what can be done, and you should also have an understanding of VBScript, as well as object oriented programming. If you don't know VBScript, you can still read on, but at some time, something that you don't understand, and didn't even know to ask about or look-up, will end up causing you a lot of grief if you try and learn VBScript by reading this and other articles.
Root Level Objects
For example, if you wanted to use the SPEAK method of the SPEECH object of the Window Eyes application COM object, you would write something like the following:
set WE = createObject ("windoweyes.application")
However, in the VBScript integrated with WE, you only have to write:
That's it! The WE application COM object is already opened for you, along with all of it's direct children objects (such as the SPEECH object in the example above), and they're all available to you as root level objects, which you do not need to dereference in order to use. The WE scripting documentation does indicate in the help topic for a given object if that object is a root level object, so that you can easily know when there is no need to dereference it.
Below is a list of root level objects; they are listed here so that you can more easily go into the scripting help documentation, and read more about them, and in particular their methods. I've found that anything which has been made into a root level object is something which you are likely to need much more frequently than the other hundred or so other objects:
Creating Your First Script
This is a good time to read the entire first major section in the WE scripting help file. We are not going to cover all of the valuable info it contains, again, in this article. However, there is one point which could use attention: how do you create the file which is going to be your first script?
The manual gives you the explanation as to why it's helpful to store your scripting files in the default location for scripts. That way, when you see the list of scripts in the script manager, you don't have to hear the complete path to each one every time you arrow past it. If it's stored in the default directory for scripts, then you only have to hear it's name. Unfortunately, this default directory is very hard to find; one reason is that it's actual path is different for each user. This is because the default directory is in the current WE profile (and you can have more than one of those), and this in turn is stored as part of the directory tree which is your Windows user profile (and this is named based on your login name). In the end, it's a very long path string, and it's some trouble to get it right, and impossible for someone to tell you ahead of time exactly what it is.
So, what I do is to go to the file menu of the WE control panel, before I go into the script manager, and choose it's User Profiles choice. We're not in this dialog to create or change user profiles; however, there is also a command button in here, who's shortcut is alt-o, which opens the directory for your current WE profile. (The name of the menu choice is a little misleading; this dialog allows you to do things with WE profiles, and not Windows user profiles, which are a different set of directories).
So, when you pressed alt-o, or clicked on the open button, a Windows Explorer window opened for the directory which contains all your scripts by default, and this is now your active window. Since this is your firstscript, you need to create the file which will hold it, and then you need to tell the script manager to load it. once it's in the script manager's list of scripts, you can edit it in the future from the script manager (with the alt-I shortcut), and you need not open this directory again unless you need to create another new file, or delete one.
To create a new script file, which is just a text file, you choose the file menu, the new sub-menu, and the text file choice. Windows Explorer will then prompt you for the new file's name, with something like "new text document.txt" already filled in, if you were to just press enter without entering a name. If you start typing this will be wiped out, but later versions of windows will still preserve the .txt extension; you however want it to have a .vbs file extension (for VBScript), and so you will need to press shift-end, wich selects the text from your current location to the end of the line, and press delete to remove this file extension. now you can enter the file name with a .vbs extension and press enter. Windows will prompt you with a yes/no question, about being sure that you really want to change the file extension from .txt. Respond with alt-Y for yes, and you're done with the file creation.
you can now go into the script manager from the WE control panel by returning to the WE control panel, where you will find the WE profiles dialog has closed, and following the steps in the scripting manual alt-F, M, and M). There is some setup work for you to do in the script manager if this is your first time in it (configuring it to see additional options, and setting your default editor).
Your script is not yet shown in the list of scripts in the script manager, you will need to set your script display to "global", and then "load" your script (detailed in the scripting help manual). At this point, you should be able to find the newly created empty file in your list of scripts; you can now highlight it, and pressing alt-I will take you into your editor of choice so that you can actually create it.
When you finish creating your script, and close the editor, your script is configured to run by default, but it will not automatically run when you close your editor. So, to get it to run you will need to "reload" it with the alt-R command.
One more thing to take note of: you may not want this new script to run each time you startup WE, and so in order to keep it from doing so, you need to disable it. To disable it, just highlight it and press the alt-E shortcut (for the enable/disable button), and it's status will changed to disabled. You may want to do this before closing down WE.
Scripting Varius Types of Issues
The remaining portion of this article is going to focus on various problems or goals that scripting might solve, and show examples of how they may be done. In each case all the WE features used will be explained. I hope in this way to be able to demonstrate all the WE scripting features and capabilities. I plan to order the issues from simpler to more complex, but I believe it's important that you read them all, and not just the one which may pertain to your immediate plans.
Having Hotkeys Trigger a Script
Virtually all scripts have this in common: they start running atsome point,they perform their initialization, and then they go into a state where they are waiting for something to happen; usually that something ultimately is the user prforming some action. When a script is in this "wait state" the WE script manager will show it's status as "running", which is a little confusing as it's not actively doing anything, it's just waiting for something to happen.
If a script isn't in this wait state, then, if it's not actively executing, then window eyes will stop the script; it assumes it has completed (and the script manager will show it's status as "stopped"). If a script however is always executing, and never goes into a wait state, then it's very likely something is wrong with it's design, as there is no reason for a script to constantly execute, endlessly, unless perhaps you're calculating Pi out to forever! Unfortunately, when a script is constantly executing, WE is unable to stop it or shut it down. This is why the wait state is so important for a script, it then allows it to receive messages, information, and instructions from WE or the oprating system. This important point, that a script is not constantly executing, but is usually waiting for something to happen, is not one which is obvious to the new scripter.
Having a script wait for the user to press a particular global hotkey is a very common way for a script to enter this wait state. It relies on capabilities of the WE object model, which allow the script to tell WE what keystroke is the hotkey, and which of the scripts subroutines or functions should automatically be executed, when the hotkey is pressed. WE also supresses the keystroke so that Windows or any application doesn't see it, and try to react to it. When you have your script call a method, and pass it the name of one of your subroutines or functions which it is to cause to be executed, then this subroutine or function is known as a "callback". A callback is required to be defined in just the specific way that the method which you are calling specifies. that is, the method you call may specify in it's documentation that the callback should b a function, with 3 parameters, defined in this order ... and when that is the case, you cannot deviate from those instructions; you must create a function, with the specified parameters (except where they are described as being optional), which returns the specified data, if you want to use the method.
In this case, the method you need to use in order to have your script respond to a hotkey is the registerHotKey method of the keyboard object. once you know this, you go to the scripting help file and lookup objects, the keyboard object, and under it methods, and finally the registerHotKey method. in it's documentation will be a specification as to how you will need to setup your callback routine for this method to use. Here is an example from the scripting help file:
' this example is a fully functional script. dim registeredKey Set registeredKey = RegisterHotKey("Control-Shift-Windows-", "SpeakHelloWorld") Sub SpeakHelloWorld() Speak "Hello world!" End Sub
Items to note about this example are:
- this is a complete and fully functional script.
- the registerHotkey and speak methods were able to be used without specifying the application object, or their parent objects, because those objects are root level objects (the parent object of the speak method is the speech object).
- the result of the call to the registerHotkey method must be stored in a variable, and this variable must be a global variable, in order for the hotkey to continue functioning. if the variable holding the result is destroyed, such as when it's a local variable and the subroutine or function ends, then the hotkey will stop functioning. this is not the only method which relies on it's result being stored in a global variable, and so you will need to pay attention to the documentation of each method for this requirement. Actually, it's not only that it keeps the hotkey functioning which is important; WE examines this script to see if it's waiting on anything, in order to decide if it's done executing and should be stopped. with the result of the registerHotkey method being stored in a global variable, this satisfies the WE requirement of "waiting on something", and so the script will be kept executing in memory, instead of being stopped. in your callback routine for example, if you were to set the global variable to nothing, destroying the result of registerHotkey, then when you returned from the callback the script would terminate.
- the result of the registerHotkey method is an object, and because it's being stored in a variable, you must use the "set" command to do so, instead of just assigning it directly to the variable.
- the hotkey being registered only remains registered as a hotkey for this session of WE running; it is not saved permanently anywhere.