Extension example

We start our example from the lecture application. This is a simple, non-networked presentation system. It is explained fully in the link just given, but we will explain it in enough detail to follow this example. By and large, the user sees the system as a set of slides, or pages, with ink on them; buttons are available to add a new page, move between pages, change ink color, and so on.

For this example, suppose we want to add this facility: We want to be able to dynamically place a button at a location on a page which, when clicked, will take us to another page. This might be useful, for example, if the current page uses a term with a complicated definition given on another page, or if the other page contains a table we need to refer to frequently. Once you've gone to the other page, you will want to be able to return to this one with a single button click.

For example, here are two pages from a presentation, which we've labelled P1 and P2 for reference below:

P1: P2:

Here the teacher is discussing the states of New England and wants to occasionally refer back to a map of the United States. In designing this feature, we have assumed that this is likely to be a spontaneous decision; the teacher realizes that there is a useful page to link to, but does not know its number.

Here's how the feature will work (many variations are possible):

  • Notice in the above images that there is a button labelled "Add link" on the regular tool bar. This was added just for the new feature (see below).

  • The instructor is currently on page P2. She places a mark (in ink) at the exact location where the link should go. (Note that there may be more than one link on a page.) She then clicks the "Add link" button. Its label changes to "Link here":

  • She then navigates to page P1 and clicks the "Link here" button:

    The display returns to page P2, and, on that page, the last ink mark is covered by a button saying "Link". The "Link here" button reverts to "Add link":

  • In the future, when the new "Link" button is clicked, the display goes to page P1 and the "Add link" button changes to "Return":

    When that button is clicked, the display returns to page P2 and the button reverts to "Add link".

We will need to change the initial XML file for this application (lecture.xml) to add the new button, and will need to write four functions:

  • AddLink - Associated with the Add Link button.
  • LinkHere - Associated with the Link Here button.
  • FollowLink - Associated with the Link button added on the linking page (P2). Even though there may be many links created, we only need one script; it will look in the button's tree node to see where it is linked to.
  • Return - Associated with the Return button.
To write these scripts, we need to know just a little bit about the lecture application. The basic shape of the lecture tree is as follows (using indentation to indicate subtree containment):

Slice (this is the root of the entire state tree)
   Lecture (w/ attribute CurrentPage)
     Slides
       InkPanel
       InkPanel
       ...

Slides are numbered by their order under the Slides node. The current page - the one currently displayed - is the unique InkPanel node whose Visible attribute is True.

This is actually all we need to know, but it is useful to know that the lecture application also provides a function GoToPageNum(page number) (where page number is a string giving the new page number). It is also useful to know that the Python global variable Lecture is initialized to point to the Lecture node (allowing us to avoid writing "Lecture = Root.GetChild("Lecture")" at the start of each script).

We need to make two changes to the XML file, lecture.xml. First, we will put the functions mentioned above in a file called Links.py; we need to add it to the PythonDefs attribute near the start of the document:

    PythonDefs="
       Lecture/scripts/init.py|
       Lecture/scripts/scripts.py|
       Lecture/scripts/Print.py|
       Lecture/scripts/Links.py"

Next, we add the Add Link button. The initial display screen for this application looks like this:

There is some space on the bottom left side, so we'll place our button there. (If there were no space, you would have to move or resize the other buttons.) The part of the XML file that describes the yellow button looks like this:

    <Button Id="YellowColorButton" W="55" H="40" X="0" Y="555"
        BackColor="Yellow" ... />

We want to add our button a bit below this, so that would be at Y=595, plus a bit for spacing:

    <Button W="55" H="40" X="0" Y="600" Text="Add Link" Id="LinkButton"
        OnClick="AddLink"/>

The button is placed as shown above. When clicked, it will call the Python AddLink function.

We now look at the functions in Links.py.

AddLink: This is clicked when the teacher is at the linking page (P2). Remember the page number by storing an attribute in the Lecture node, and change the Add Link button to Link Here. The slide number of the current slide, counting from zero, is the position of the current slide among its siblings; it needs to be converted to a string to store it as an attribute.

    def AddLink ():
      Lecture["LinkingPage"] = str(GetCurrentSlide().Position())
      ChangeLinkButton("Link Here", "LinkHere")
      return

The ChangeLinkButton is used in several of the functions. Note that there is just one button in this application, but we keep changing its label and its associated Python function. ChangeLinkButton finds the button by using its Id field, and then changes its label and its associated script name.

    def ChangeLinkButton (label, script):
      buttonnode = TreeNode.FindNodeById("LinkButton")
      buttonnode["Text"] = label
      buttonnode["OnClick"] = script
      return

LinkHere: The teacher navigates to the page she wants to link to (P1), and clicks the Link Here button, which calles the LinkHere function. This is the most complicated function in this feature because it has to find out where to put the Link button and then put it there.

    def LinkHere ():
      linkedpage = GetCurrentSlide().position()
      linkingpage = Lecture["LinkingPage"]
      GoToPageNum(int(linkingpage))
      linkingpagenode = Slides[int(linkingpage)]
      strokes = linkingpagenode.GetChildren()
      laststroke = strokes[len(strokes)-1]
      X = laststroke["X"]
      Y = laststroke["Y"]
      linkbuttonnode = tree("Button", ["Text", "Link", "X", X, "Y", Y, "W", 40,
           "H", 30, "LinkTo", linkedpage, "OnClick", "FollowLink"], [])
      linkingpagenode.AppendChild(linkbuttonnode)
      ChangeLinkButton("Add Link", "AddLink")
      return

Keep in mind the distinction between the "linked" page (P1) and the "linking" page (P2). This script goes back to the linking page, finds the last ink stroke that was entered on that page, creates a new Button node using the same (X,Y) coordinates as the stroke, adds it to the page, and then changes the "Link Here" button back to "Add Link". If you look back at the structure of the lecture tree, you should be able to follow the code. Some tricky points to note:

  • Attributes are always strings. When an string contains a number (like linkingpage), it has to be converted to an integer before any arithmetic can be done on it. That is why we have to say "int(linkingpage)" on line 4.

  • The "tree" function used on line 10 creates a new node with the name given as the first argument and the list of attributes and values given in the second argument. (Note that we've included the page this links to as the "LinkTo" attribute.) The third argument, which is just the empty list [] here, can be used to give subtrees. This function is defined in init.py.

FollowLink: This is the script invoked when the Link button is clicked. The node we created contains the number of the page to link to in its LinkTo attribute. This script needs to record the number of the page we're linking from so that the Return script can return to it.

    def FollowLink ():
      Lecture["LinkingPage"] = str(GetCurrentSlide().position())
      GoToPageNum(int(Source["LinkTo"]))
      ChangeLinkButton("Return", "Return")
      return

Return: Since the linking page's number was recorded when the link was followed, this script just finds it in the Lecture node. The toolbar button reverts to its original Add Link version.

    def Return ():
      GoToPageNum(int(Lecture["LinkingPage"]))
      ChangeLinkButton("Add Link", "AddLink")
      return

That's all there is to it.

    Last updated on Sat Feb 21 11:58:02 CST 2009