.. rst3: filename: html/tutorial-scripting.html .. |---| unicode:: U+02015 .. for quotes :trim: ############################# 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 codebase. **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. .. _`Leo's cheat sheet`: cheatsheet.html .. _`scripting portion`: cheatsheet.html#scripting **Further study**: The `scripting portion`_ of `Leo's cheat sheet`_ contains more information about scripting. .. contents:: Contents :depth: 3 :local: Hello world +++++++++++ .. index:: pair: Hello World Example; Tutorial Here is the obligatory "Hello World!" script: .. code-block:: python g.es('Hello World!') # g.es prints all its arguments to the log pane. 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. **Important** If text is selected, execute-script executes only the selected text. If you are in LeoDocs.leo, you can run the script from this node. Create outline nodes ++++++++++++++++++++ **p.b** is the body text associated with position p. Similarly, **p.h** is p's headline. p.b and p.h are python properties, so you can assign to p.b and p.h. This script creates an outline node as the last top-level node: .. code-block:: python p = c.lastTopLevel().insertAfter() p.h = 'my new node' c.redraw(p) # Selects the new node. This script creates multiple nodes, with different headlines: .. code-block:: python parent = c.lastTopLevel().insertAfter() parent.h = 'New nodes' table = ( ('First node', 'Body text for first node'), ('Node 2', 'Body text for node 2'), ('Last Node', 'Body text for last node\nLine 2'), ) for headline, body in table: child = parent.insertAsLastChild() child.b = body.rstrip() + '\n' # Ensure exactly one trailing newline. child.h = headline c.selectPosition(parent) # Another way to select nodes. c.redraw() This script creates a node containing today's date in the body text: .. code-block:: python import time p = c.lastTopLevel().insertAfter() p.h = "Today's date" p.b = time.strftime("%Y/%m/%d") c.redraw(p) Generate an output file from nodes ++++++++++++++++++++++++++++++++++ The script writes the body text of the presently selected node to ~/leo_output_file.txt and then prints it to the log pane: .. code-block:: python fn = g.os_path_finalize_join(g.app.homeDir, 'leo_output_file.txt') with open(fn, 'w') as f: f.write(c.p.b) with open(fn, 'r') as f: for line in f.readlines(): g.es(line.rstrip()) Predefined symbols: c, g, and p +++++++++++++++++++++++++++++++ .. index:: pair: Predefined Symbols; Tutorial 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 ++++++++++++++++++++ .. index:: pair: Position; Tutorial pair: position class; Tutorial pair: Vnode; Tutorial pair: vnode class; Tutorial .. _`Python properties`: https://stackabuse.com/python-properties/ 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 by all positions referring to that node. .. _`user attributes`: customizing.html#adding-extensible-attributes-to-nodes-and-leo-files 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. Generators ++++++++++ .. index:: pair: Generator; Tutorial 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: .. code-block:: python for p in c.all_positions(): print(' '*p.level()+p.h) .. index:: pair: Invalid positions; Tutorial Scripts may capture positions like this: .. code-block:: python aList = list(c.all_positions()) All of Leo's generators now yield *copies* of 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 (instance variable), 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! Summary +++++++ - 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. For more information, consult `Leo's cheat sheet `_ and `Leo's scripting miscellany `_