Writing Leo scripts in Python

This chapter tells how to write Leo scripts, Python scripts run from any Leo node. This chapter is intended for those fairly comfortable with Python scripting. If you are not, please study the excellent Python Tutorial. Jacob Peck has written a more informal scripting tutorial.

Three predefined symbols, c, g and p give Leo scripts easy access to all the data in the outline. These symbols also allow Leo scripts to execute any code in Leo’s own code base.

Positions and vnodes are the foundation of Leo scripting. leo/core/leoNodes.py defines the corresponding Position and VNode classes. These classes provide access to all outline data, and allow Leo scripts to create and change outlines.

Further study: The scripting portion of Leo’s cheat sheet contains more information about scripting.

Hello world

Here is the obligatory “Hello World!” script:

g.es('Hello World!')

In more detail:

  1. Create a node anywhere in the outline.
  2. Put g.es(‘hello, world!’) in the node’s body text.
  3. Select the node and type Ctrl-B.


  • If text is selected, execute-script executes only the selected text.
  • g.es prints its arguments to Leo’s log pane.

Predefined symbols: c, g and p

The execute-script command predefines the symbols c, g and p.

c is the commander of the outline containing the script. Commanders are instances of the Commands class, defined in leoCommands.py. Commanders provide access to all outline data and all of Leo’s source code.

g is Leo’s leo.core.leoGlobals containing many useful functions, including g.es.

p is the position of the presently selected node. Positions represent nodes at a particular location of an outline. Because of clones, the same node may appear at multiple positions in an outline. c.p is the outline’s presently selected position.

Positions and vnodes

A position represents an outline node at a specific position in the outline. Positions provide methods to insert, delete and move outline nodes. The scripting portion of Leo’s cheat sheet lists the most important methods of the position class.

Because of clones, the same node may appear at multiple positions in the outline. A vnode represents the node’s data, which is shared all positions referring to that node.

For any position p, p.b is the node’s body text, p.h is the node’s headline and p.u is the node’s user attributes, and p.v is the position’s vnode. Similarly, for any vnode v, v.b is the node’s body text, v.h is the node’s headline and v.u is the node’s user attributes.


Commanders and positions define several Python generators to traverse (step through) an outline. The scripting portion of Leo’s cheat sheet lists all of Leo’s generators. For example, c.all_positions() traverses the outline in outline order. The following prints a properly-indented list of all headlines:

for p in c.all_positions():
    print(' '*p.level()+p.h)

Scripts may capture positions like this:

aList = list(c.all_positions())

Warning: stored positions become invalid when outline changes. c.positionExists(p) is True if p is valid in c’s outline.

wrappers vs. widgets

Leo’s Gui code is built on wrapper and widget classes. A widget is an actual Qt widget. A wrapper is an object whose API hides the details of the underlying gui text widgets. Leo’s core code usually uses wrappers, not raw widgets.

There is a back door for special cases. All wrapper classes define an official widget ivar, so core or plugin code can gain access to the real Qt widget using wrapper.widget. Searching for wrapper.widget should find all gui-dependent snippets of code in Leo’s core.

Wrappers allow the same text-handling code to work regardless of whether the actual text widgets are a QTextBrowser or a QsciScintilla object. Without wrappers, all of Leo’s text-editing commands would have to know the details of the api of the actual Qt text widget!


  • execute-script predefines c, g and p.
  • c is a commander, g is the leoGlobals module, and p is the current position.
  • Vnodes contain all outline data.
  • Positions provide easy access to vnodes.
  • Positions become invalid when outline nodes are inserted, deleted or moved.
  • Generators visit all or parts of the outline, in a specified order.