| A YASARA plugin is a Python script or Yanaconda macro that adds menus and options to the YASARA user interface and gets executed when you activate the respective option. Plugins are the ideal solution to add your own favorite functions to YASARA without having to play around with compilation,
C and Assembler code. All plugins are collected in the plg/ subdirectory,
for latest developments visit the plugin repository at www.yasara.org/plugins
. Even though YASARA has its own built-in macro language,
there are some applications where Yanaconda macros
cannot help, like web-services and database-interfaces. Python is a well established,
user friendly scripting language with excellent support for the above topics and was therefore the first choice. Support for other languages can be added quickly,
so if you are not happy with Python, send a mail with the subject "Plugin Language" to eliza@yasara.org.
If you are a Linux or MacOSX user, Python is already present. If you are running Windows,
it depends on your installation. Try to click Help > Install program > Python. If this option is not available,
Python is already installed.
All plugins must be stored in YASARA's 'plg/' subdirectory. Look at some of the files present there and pick the one closest to your needs as a programming scaffold.
 | The first line of a YASARA Python plugin must always read
'# YASARA PLUGIN'. Adapt the remaining fields to describe your plugin. This information will be used if you decide to add it to the YASARA plugin repository at
www.yasara.org/plugins in return for YASARA thank-you credits. Go to
www.yasara.org/contribute to submit your plugin.
# YASARA PLUGIN
# TOPIC: Database interfaces
# TITLE: Retrieve a PDB file by FTP
# AUTHOR: Elmar Krieger
# LICENSE: GPL (www.gnu.org)
# DESCRIPTION: This plugin asks for a PDB ID and retrieves the file by FTP
#
After the header, the plugin specifies where to add which entries to YASARA's user interface to make selections and run the plugin. These definitions will be read when YASARA registers the plugin during startup and must be enclosed in triple quotes
("""), so that Python ignores them. If you make changes here, you must restart YASARA. The indentation has to be a multiple of two spaces,
Tabs are not allowed. Here is an example:
"""
MainMenu: File
PullDownMenu after Load: _G_et by FTP
TextInputMenu1: Retrieve a PDB file by FTP
Text: Please input the four letter PDB ID code
Text: _P_DB ID
Request: GetByFTP
"""
The example above tells YASARA to:
- Enter the main menu, search for entry 'File' (add it if not found).
- Enter a pull down menu, search for entry 'Get by FTP' right after entry 'Load' (add it if not found).
- Add a text input menu with 1 input field and the header 'Retrieve a PDB file by FTP'.
- Within the text input menu, print the text 'Please input the four letter PDB ID code'.
- On top of the first (and only) text input box, print the text 'PDB ID' with the 'P' underlined.
- Run the plugin with the request 'GetByFTP'
The tripple quote comment may contain the following keywords to identify certain menu types:
 | The indentation level for this keyword must be
0. You can use 'after' and 'before' to specify the location of the new option. Underscores surround underlined characters.
Examples:
MainMenu: File
MainMenu after Effects: A_n_imations
MainMenu before Analyze: _T_ools
The atom context menu appears when you click on a marked atom with the right mouse button,
the indentation level for this keyword must be 0. You can use 'after' and 'before' as known from
MainMenu. Example:
AtomContextMenu: _Q_uery mutation effects
The residue context menu appears when you click on a residue in the sequence selector with the right mouse button,
the indentation level for this keyword must be 0. You can use 'after' and 'before' as known from
MainMenu. Example:
ResidueContextMenu before Label: _Q_uery mutation effects
This keyword must only be used after MainMenu
with an indentation level of 1. Example:
MainMenu: File
PullDownMenu after Load: _G_et by FTP
Pop-up menus are little windows popping up on the side of pull-down menus or context menus. This keyword must therefore only be used after
PullDownMenu, AtomContextMenu
or ResidueContextMenu, with an indentation level of
1 or 2. Example:
MainMenu: View
PullDownMenu: Color
PopUpMenu: by properties
This keyword adds a standard object selection menu, the selections are passed to the Python plugin via
object descriptors. The specified text appears as the window header. Yanaconda plugins can currently not access these selections.
The jth object descriptor in the ith selection window can be accessed as yasara.selection[i].object[j]
( see below). Example:
MainMenu: Analyze
PullDownMenu: _M_CSIS mutations
ObjectSelectionMenu: Select objects to map mutations stored in the MCSIS
Request: MapMutations
The jth molecule descriptor in the ith selection window can be accessed as yasara.selection[i].molecule[j]
( see below). Example:
MainMenu: Analyze
PullDownMenu: _C_ompare amino acid sequence of two molecules
MoleculeSelectionMenu: Select first molecule to compare sequence
MoleculeSelectionMenu: Select second molecule to compare sequence
Request: CompareSequence
The jth residue descriptor in the ith selection window can be accessed as yasara.selection[i].residue[j]
( see below). Example:
MainMenu: Analyze
PullDownMenu: _P_roscan
ResidueSelectionMenu: Select residues for ProScan
Request: Proscan
The jth atom descriptor in the ith selection window can be accessed as yasara.selection[i].atom[j]
( see below). Example:
MainMenu: View
PullDownMenu: Color
PopUpMenu: by force
AtomSelectionMenu: Select atoms to color by force
Request: ColorByForce
This keyword adds a window with one to four text input boxes. In addition to the window header,
you must specify a general explanation for the user and then one header for each text input box.
The content of the jth text input box in the ith selection window can be accessed by the Python plugin as yasara.selection[i].text[j]
( see below). Example for a window with two text input boxes:
MainMenu: Options
PullDownMenu: _R_eport error
TextInputMenu2: Send an error report by e-mail
Text: Please give a brief description of the problem:
Text: _D_escription part 1 (header for the first input box)
Text: _D_escription part 2 (header for the second input box)
This keyword adds a window with one to six number input boxes. In addition to the window header,
you must specify a general explanation text and then one descriptor for each number input box. A number descriptor contains four elements,
separated by commas: the title of the number box, the default value, the minimum allowed value and the maximum allowed value.
The content of the jth number input box in the ith selection window can be accessed by the Python plugin as yasara.selection[i].number[j]
( see below). Example for a window with one number input box:
MainMenu: Edit
PullDownMenu after Build: Sample
PopUpMenu: _O_bject
ObjectSelectionMenu: Select protein to sample conformational space with CONCOORD
NumberInputMenu1: Select ensemble size
Text: Number of structures in the CONCOORD ensemble:
Number: _S_tructures,10,1,98
Request: SampleObj
This keyword adds a window with one to five switch boxes,
where exactly one box can be selected. This allows to choose between up to five exclusive options,
you must specify a general explanation for the user and then one additional text for every switch box.
The number of the hooked box in the ith selection menu can be accessed by the Python plugin as yasara.selection[i].switchbox
( see below). Example for a menu with two switch boxes:
MainMenu: Options
PullDownMenu: _R_eport error
SwitchBoxMenu2: Concretize the error
Text: Did the problem occur right now?
Text: _Y_es, I did not exit YASARA since then.
Text: _N_o, just before, I had to restart YASARA to get here.
This keyword adds a window with one to five check boxes,
that can be activated individually. This allows to toggle up to five independent options,
you must specify a general explanation for the user and then one additional text for every check box.
The state of the jth check box in the ith selection menu can be accessed by the Python plugin as yasara.selection[i].checkbox[j]
( see below). Example for a menu with two check boxes:
MainMenu: NMR
PullDownMenu: _L_ist restraints
CheckBoxMenu2: List distance and dihedral angle restraints
Text: Select the type of restraints to list
Text: Distance restraints
Text: Dihedral restraints (Checked)
By default, all boxes are unchecked. To check a box, add the text
'(Checked)' at the end as in the example above. This keyword adds a window with a list of options. Set the
'MultipleSelections' flag to 'Yes' if the user is allowed to select more than one list entry and to
'No' otherwise. The first text is displayed above the list, the other texts are the actual list entries.
The jth selected list entry in the ith selection menu can be accessed by the Python plugin as yasara.selection[i].listentry[j]
( see below). Example:
MainMenu: Analyze
PullDownMenu: _P_DBFinder2 properties
ResidueSelectionMenu: Select residues to color by PDBFinder2 properties
ListMenu: Select PDBFinder2 properties
MultipleSelections: Yes
Text: Select more than one list entry to color by the average value
Text: Nalign - Number of HSSP alignments
Text: Nindel - Number of insertions and deletions
Text: Entropy - HSSP sequence entropy
Request: ColorResidues
This keyword adds a window with a file browser. Set the 'MultipleSelections' flag to
'Yes' if the user is allowed to select more than one list entry and to 'No' otherwise. The Filename keyword specifies a wildcard with the initial path. Use forward slashes also under Windows.
The jth selected filename in the ith selection menu can be accessed by the Python plugin as yasara.selection[i].filename[j]
( see below). Example:
MainMenu: File
PullDownMenu: Load
PopUpMenu after PDB file: _N_MR ensemble
FileSelectionMenu: Select a PDB file containing an NMR ensemble
MultipleSelections: No
Filename: pdb/*.pdb
Request: LoadEnsemble
| When using Yanaconda, the rest of the plugin is a straightforward macro. The variable
'Request' contains the specified request, e.g. 'GetByFTP' and can be used to execute different parts of the macro. Selections can currently not be accessed.
When using Python, the first command must be Immediately afterwards, you can access the data passed to the plugin as well as call YASARA functions. The predefined variables are listed below,
the most important one is yasara.request, a string indentifying the user's request. In the above example,
there is only one possible request: 'GetByFTP'. When YASARA starts up and registers the plugins,
it also sends a 'CheckIfDisabled' request, giving the plugin the possibility to exclude itself from registration.
The remaining plugin scaffold therefore looks like that:
if (yasara.request=="CheckIfDisabled"):
# ASSIGN A 1 TO yasara.plugin.exitcode IF THIS PLUGIN CANNOT WORK AND SHOULD
# BE DISABLED (DATA MISSING, WRONG OPERATING SYSTEM ETC.)
if (....) yasara.plugin.exitcode=1
elif (yasara.request=="GetByFTP"):
# DO THE WORK
# END THE PLUGIN, MUST BE THE LAST COMMAND
yasara.plugin.end()
|
For Yanaconda plugins this is trivial, as they are just macros. For Python plugins,
the YASARA functions are wrapped so that they can be accessed with a syntax that matches Python's requirements.
Examples:
Font Arial,Height=2,Spacing=1.5,Color=Yellow,Depth=5,DepthCol=Red
becomes
yasara.Font("Arial",height=2,spacing=1.5,color="Yellow",depth=5,depthcol="Red")
The documentation page of each YASARA command lists the prototype of the corresponding Python function,
e.g. the Font command (look at the 'Python:' row in the table at the top of each page).
A few YASARA commands support more than one format with different argument types. This is not possible in Python,
the command thus has to be wrapped by different Python functions. The names of these Python functions differ at the end,
using either an increasing number or the name of the first argument. More details
are available here. Many YASARA commands return results, which can at the moment not be passed to Python plugins
(however they can be passed to Python scripts that use the YASARA module
). Making this possible also for plugins is a top priority, in the mean time you have to use a Yanaconda-based workaround. Instead of passing the results to Python variables,
you use yasara.run to store them in Yanaconda variables: So instead of the obvious way:
# Load a PDB file and color it red, THIS DOES NOT WORK, JUST FOR FUTURE REFERENCE
obj=yasara.LoadYOb("1crn")
yasara.ColorObj("%d,Red"%obj)
you have to use:
# Load a PDB file and color it red
yasara.run("obj = LoadPDB 1crn")
yasara.ColorObj("(obj),Red")
If the YASARA command returns a list of results, and you need to loop over this list,
the currently only solution is to run the entire loop in Yanaconda, using the no-operation command
'pass' to move the indentation level back and indicate the end of the loop:
# Get a list of all residue numbers
yasara.run("reslist() = ListRes all")
# Print it in the YASARA console
yasara.run("for i=1 to count reslist")
yasara.run(" print 'Residue number (i) is (reslist(i))'")
yasara.run("pass")
Right after the 'import yasara' statement,
the following variables can be accessed. Note that [i] specifies the number of the selection window,
counting starts with zero at each chain of selection windows leading to a 'Request' keyword.
[j] specifies the number of the selected item, counting starts with zero in every selection window.
|
yasara.request
| The request string sent by YASARA to the plugin |
yasara.opsys
| The current operating system, "Linux", "MacOS" or
"Windows" |
yasara.version
| The YASARA version string X.Y.Z |
yasara.serialnumber
| YASARA's serial number |
yasara.stage
| The YASARA stage View, Model, Dynamics or Structure |
yasara.plugin.name
| The name of the plugin (e.g. ftppdb.py) |
yasara.plugin.config
| A Python dictionary with the options from the plugin config file
*.cnf |
yasara.plugin.exitcode
| The exit code returned to YASARA when the plugin ends |
yasara.owner.firstname
| Your first name |
yasara.owner.email
| Your e-mail address |
yasara.selection
| A list of selections with one entry for every selection menu you defined |
yasara.selection[i].objects
| The number of objects selected in the ith selection menu |
yasara.selection[i].object[j]
| The obj_descriptor (see below) for the jth selected object in the ith selection menu |
yasara.selection[i].molecules
| The number of molecules selected in the ith selection menu |
yasara.selection[i].molecule[j]
| The mol_descriptor (see below) for the jth selected molecule in the ith selection menu |
yasara.selection[i].residues
| The number of residues selected in the ith selection menu |
yasara.selection[i].residue[j]
| The res_descriptor (see below) for the jth selected residue in the ith selection menu |
yasara.selection[i].atoms
| The number of atoms selected in the ith selection menu |
yasara.selection[i].atom[j]
| The atom_descriptor (see below) for the jth selected atom in the ith selection menu |
yasara.selection[i].texts
| The number of text input boxes in the ith selection menu |
yasara.selection[i].text[j]
| The text typed into the jth text input box in the ith selection menu |
yasara.selection[i].numbers
| The number of number input boxes in the ith selection menu |
yasara.selection[i].number[j]
| The number typed into the jth number input box in the ith selection menu |
yasara.selection[i].checkboxes
| The number of checkboxes in the ith selection menu |
yasara.selection[i].checkbox[j]
| The state of the jth checkbox in the ith selection menu (1=hooked,
0=not hooked) |
yasara.selection[i].switchbox
| The number of the hooked switchbox in the ith selection menu,
None if there was no switchbox |
yasara.selection[i].listentries
| The number of selected list entries in the ith selection menu |
yasara.selection[i].listentry[j]
| The jth selected list entry in the ith selection menu |
yasara.selection[i].filenames
| The number of selected filenames in the ith selection menu |
yasara.selection[i].filename[j]
| The jth selected filename in the ith selection menu |
|
 | Object descriptors are instances of the class obj_descriptor. Typically,
you loop over all object descriptors in the ith selection menu:
for j in range(yasara.selection[i].objects):
object=yasara.selection[i].object[j]
And then access various object properties: |
object.name
| The name of the object |
object.number.inyas
| The unique number/ID of the object in YASARA (a string, starting with
1) |
object.number.inall
| The sequential number of the object in the soup (a string, starting with
1) | |
You could then color the object red:
yasara.ColorObj(object.number.inyas+",Red")
Molecule descriptors are instances of the class mol_descriptor. Typically,
you loop over all molecule descriptors in the ith selection menu:
for j in range(yasara.selection[i].molecules):
molecule=yasara.selection[i].molecule[j]
And then access various molecule properties: |
molecule.name
| The name of the molecule (that's the chain name in the PDB file) |
molecule.number.inyas
| The unique number/ID of the molecule in YASARA (a string) |
molecule.number.inall
| The sequential number of the molecule in the soup (a string,
starting with 1) |
molecule.number.inobj
| The sequential number of the molecule in the object (a string,
starting with 1) |
molecule.object
| The object descriptor
for the object the molecule belongs to | |
You could then display the molecule as sticks:
yasara.StickMol(molecule.number.inyas)
Or delete the entire object containing this molecule:
yasara.DelObj(molecule.object.number.inyas)
Residue descriptors are instances of the class res_descriptor. Typically,
you loop over all residue descriptors in the ith selection menu:
for j in range(yasara.selection[i].residues):
residue=yasara.selection[i].residue[j]
And then access various residue properties: |
residue.name3
| The name of the residue in three letter code. |
residue.name1
| The name of the residue in one letter code. |
residue.number.inyas
| The unique number/ID of the residue in YASARA (a string). |
residue.number.inall
| The sequential number of the residue in the soup (a string, starting with
1). |
residue.number.inobj
| The sequential number of the residue in the object (a string,
starting with 1). |
residue.number.inmol
| The sequential number of the residue in the molecule (a string,
starting with 1). |
residue.number.inpdb
| The number of the residue in the PDB file (a string, last character may be the insertion code). |
residue.object
| The object descriptor
for the object the residue belongs to. |
residue.molecule
| The molecule descriptor
for the molecule the residue belongs to. | |
You could then color the residue yellow:
yasara.ColorRes(residue.number.inyas+",Yellow")
Or display a ribbon for the entire molecule containing this residue:
yasara.ShowSecMol(residue.molecule.number.inyas,"Ribbon")
If you wonder why a '+' is used to combine the arguments in the first case,
while a comma ',' is used in the second case, look here
.
Atom descriptors are instances of the class atom_descriptor. Typically,
you loop over all atom descriptors in the ith selection menu:
for j in range(yasara.selection[i].atoms):
atom=yasara.selection[i].atom[j]
And then access various atom properties: |
atom.name
| The name of the atom |
atom.namespaced
| The name of the atom including spaces (always four characters) |
atom.altloc
| The alternate location indicator of the atom |
atom.position
| The position of the atom, a list with three cartesian coordinates |
atom.occupancy
| The occupancy field of the atom in the original PDB file |
atom.bfactor
| The B-factor of the atom |
atom.number.inyas
| The unique number/ID of the atom in YASARA (a string). |
atom.number.inall
| The sequential number of the atom in the soup (a string, starting with
1, the same as .inyas). |
atom.number.inobj
| The sequential number of the atom in the object (a string, starting with
1, usually the same number as in the PDB file). |
atom.number.inmol
| The sequential number of the atom in the molecule (a string,
starting with 1). |
atom.number.inres
| The sequential number of the atom in the residue (a string, starting with
1). |
atom.object
| The object descriptor
for the object the atom belongs to. |
atom.molecule
| The molecule descriptor
for the molecule the atom belongs to. |
atom.residue
| The residue descriptor
for the residue the atom belongs to. | |
You could then color the atom green:
yasara.ColorAtom(atom.number.inyas+",Green")
Or delete the entire residue the atom belongs to:
yasara.DelRes(atom.residue.number.inyas)
|
While a Python plugin is running, you can continue using YASARA normally. There are in fact two threads working in parallel: YASARA and the plugin. When a plugin runs a YASARA command,
this command is passed from the plugin to YASARA and executed as soon as possible.
It is NOT guaranteed that YASARA has finished a command when the function call in the plugin returns.
This can lead to potential problems if there is a data dependency between YASARA and the plugin,
usually involving files on the hard drive accessed by both threads. In such cases,
it is therefore essential that the plugin waits for YASARA to finish execution. This is done by calling the
'Finish' function:
- The plugin reads a file created by YASARA:
Starting with YASARA 4.8.5, the Finish() function is called automatically by most critical YASARA commands that write to the hard drive and is thus not needed anymore.
The only exception is the LogAs command:
# Log the output of the next command
yasara.LogAs("MyLog")
# List all lysine residues
yasara.ListRes("Lys")
# Finish writing the log file
yasara.Finish()
# Read the log file in Python
log=open("MyLog").readlines()
- YASARA reads a file created by the plugin:
This is normally not a problem, unless the file is a temporary one,
and the plugin decides to delete it. In this case, the plugin must wait for YASARA to read the file before deleting it:
# DOWNLOAD A PDB FILE FROM THE WEB
pdb=urllib.urlopen(url).readlines()
# SAVE IT TEMPORARILY
open(pdbfilename,"w").writelines(pdb)
# READ IT IN YASARA
yasara.LoadPDB(pdbfilename)
# FINISH READING
yasara.Finish()
# DELETE THE TEMPORARY FILE
os.path.remove(pdbfilename)
Instead of waiting for YASARA, one can also let YASARA delete the file,
which avoids synchronization issues:
# READ IT IN YASARA
yasara.LoadPDB(pdbfilename)
# AND DELETE
yasara.DelFile(pdbfilename)
Normally YASARA executes each command issued by the plugin just as if it had been created via the graphical user interface. This includes an update of the graphics display and the undo history after each command. If the plugin issues hundreds of commands,
this approach may become too slow. In this case resort to the trick used by Yanaconda macros to speed up execution: just switch off the
console. Plugins are normally linked to options in the user interface. Sometimes,
it may be helpful to run a plugin directly. This is achieved with the RunPlugin
command in connection with SavePLI
:
# Save a PLugin Input (PLI) File containing a selection of Calpha atoms
# and the request 'MyRequest'
SavePLIAtom CA,MyRequest
# Run the plugin
RunPlugin MyPlugin.py
If no selections are required, the plugin can also be run from the command line:
yasara PathToMyPlugin/MyPlugin.py MyRequest
Without any selections, no graphical user interface is needed,
and plugins can also be run in console mode:
yasara -con PathToMyPlugin/MyPlugin.py MyRequest
If you want to exit YASARA as soon as the plugin has finished,
add this line to the end of the plugin:
yasara.Exit()
Programs contain errors, the same is true for plugins. There are two types of errors in Python plugins:
- Errors that occur during the initial plugin registration when YASARA starts up.
Most of the time these are simple syntax errors.
In Linux and MacOSX, you see the error message in the console from where you started YASARA.
Windows can unfortunately not display the error message, but you know that something
went wrong because your plugin does not appear in YASARA's user interface. Open a command
prompt, go to the yasara\plg directory and run the plugin directly with the Python
interpreter to locate the problem:
c:\MyPythonInstallationPath\python.exe MyPlugin.py
This will show you a traceback. After correcting the error you have to restart YASARA.
- Errors that occur while the plugin is running. YASARA displays the
main error message on screen, and a complete traceback in the console which you
can bring up by pressing <SPACE>. After correcting the error, you can simply rerun
the plugin, you DO NOT have to restart YASARA.
If you want to print debug statements to trace a problem,
this is easily done using
print "MyMessage"
in Yanaconda and
yasara.write(WhatEver)
in Python plugins. 'WhatEver' does not have to be a string, but just anything you can pass to Python's print function. DO NOT use Python's print function directly,
because this fails under Windows unless you also flush the output buffer with sys.stdout.flush()
If your Python plugin hangs in an infinite loop, click on Options
> Stop plugin. This will terminate your plugin as soon as it tries to print something or calls a YASARA command. If the plugin does not do any of these things,
YASARA will also hang until you kill the Python task manually from the Windows Task Manager,
with the Linux 'kill' command or with the MacOSX 'Activity Monitor' (can be found in the Applications/Utilities folder).
If you add an extra user interface to your plugin using the Tkinter framework
, you may encounter the problem that fatal errors occurring in your plugin are not reported by YASARA immediately. The reason is that Tkinter does not terminate after an error,
and also does not flush the output buffer properly, so that error messages get stuck and reach YASARA only when your plugin truly exits. The solution is to force Tkinter to exit when an error occurs:
- Open the file Tkinter.py, which can usually be found at /usr/lib/python2.4/lib-tk/Tkinter.py,
and append the command 'raise SystemExit' at the end of the __call__ method in class CallWrapper,
so that it looks like that:
class CallWrapper:
def __init__(self, func, subst, widget):
self.func = func
self.subst = subst
self.widget = widget
def __call__(self, *args):
try:
if self.subst:
args = self.subst(*args)
return self.func(*args)
except SystemExit, msg:
raise SystemExit, msg
except:
self.widget._report_exception()
raise SystemExit <<<< Newly added!
|