Interacting with Maestro¶
Basic Concepts¶
In order to effectively write scripts that control Maestro a basic understanding of Maestro concepts is needed. The two most important concepts are the Project Table and the Workspace.
A Maestro project is a collection of Structure objects and associated data, and the Project Table is the way in which this data is viewed and modified. The Project Table contains zero or more rows. Each row is referred to as an entry and consists of a Structure object (a collection of atoms) and zero or more properties that appear as columns in the table. Each column in the Project Table corresponds to a different property. Acceptable property types are: Boolean, integer, floating point, and string. Properties are most commonly produced from computational jobs but can also be generated by Maestro or added manually by users.
The Workspace is the 3D molecular display area in the main Maestro window. The 3D items displayed in this area include the molecular structure, labels, measurements, ribbons, markers, and surfaces. You can include multiple Project Table entries simultaneously into the Workspace for viewing, analysis or modification. Note that Project Table entries loaded into the Workspace contain only their structural information, and so Structure objects retrieved via the Workspace do not have properties available.
It is important to understand that entries (structures) that have been modified in the Workspace are not immediately updated in the Project Table. By default, updates happen when the entry is implicitly or explicitly excluded from the Workspace. Thus, when writing Python scripts in which you manipulate an entry in the Workspace, you need to synchronize the Workspace changes with the Project Table before using a structure entry from the Project Table. For safety, you should also synchronize the Workspace and Project Table for cases where you simply want to get the structural information for an entry in the Project Table. You can also register a “Workspace changed” callback function to know when synchronization with the project is required.
In contrast with structural changes, entry properties are immediately updated in the project when they are edited in the Project Table.
IPython in Maestro¶
The Python Shell available in the Window menu is a full-featured IPython
terminal, with the maestro
module already imported. You can interact with the
workspace structure, the project table and more. The IPython terminal also
displays 2D representations of small molecules.
Running Scripts from Maestro¶
From the Maestro Command Input Area, you can run individual functions from
modules, but you can’t run scripts as a whole. The following Maestro command
will run function func
from script script.py
:
pythonrun script.func
The script.py
file must be in the directory Maestro was launched
from.
Arguments can also be passed to the functions. For example, if function1
takes one argument and function2
takes two arguments:
pythonrun script.function1 5
pythonrun script.function2 3 5
The pythonrun can only interpret its arguments as integers, floats, and
strings. Numbers with decimal points or in scientific notation are converted
to floats, other numbers are converted to integers, and everything else is a
string. (To pass numbers as strings, enclose them in single quotes.) This
means that there is no support for use of None
, True
or False
,
lists, tuples, etc. — they are all converted to strings. Use pythoneval
for situations where this limitation is not acceptable.
The pythonrun
command works by first importing the module, then running
the specified function. So, if you make changes to your module
script.py
, you need to force Maestro to import it again to see the
changes. This can be done via:
pythonimport script
In addition to pythonrun
and pythonimport
, the Maestro command
pythoneval
is available and can take python commands in standard
syntax. For example:
pythoneval import script
pythoneval script.function(5, True, ['a', 4])
Use of pythoneval
is roughly equivalent to typing commands on the
interactive prompt. Use the Python builtin function reload
to re-import a
modified module.
Note that you can define aliases in the Maestro Command Input Area. For
example, to make py
an alias for pythoneval
, run:
alias py pythoneval
The Maestro Module¶
The Maestro module contains functions for interacting with Maestro. Note
that the Maestro module may only be used for scripts that are running inside
Maestro scripts. Trying to import schrodinger.maestro
outside of Maestro
will result in an ImportError
.
Executing Maestro Commands from a Script¶
You can use the same commands you would type in the Maestro Command Input Area from within Python:
from schrodinger.maestro import maestro
def rotate_by(by=90):
maestro.command(f"rotate y={by}")
The hundreds of available commands and their options are documented in the Maestro Command Reference Manual.
Note
You can review the commands that have been issued in the normal operation of Maestro by choosing Command Script Editor from the Edit menu.
There are several different ways the maestro.command function may be called, see the command function for more details.
One useful group of Maestro commands is markers
, showmarkers
and
hidemarkers
. These can be used to define a marker group, and then to
show or hide them. Syntax is as follows:
markers <marker name> <ASL> red=<value> blue=<value> green=<value>
showmarkers <marker name>
hidemarkers <marker name>
Interacting with the Workspace¶
The workspace_get and workspace_set functions let a script get a Structure from and send a Structure to the Maestro workspace.
from schrodinger.maestro import maestro
from schrodinger import structure
st = maestro.workspace_get()
# ... manipulate structure here ...
maestro.workspace_set(st)
Note
The workspace_get
function does not preserve the properties of entries
in the workspace that are included from the Project Table. To obtain
included project table entries and their properties, use the
included_rows
iterator described in the next section.
Interacting with the Project Table¶
The project module allows Python scripts to interact with the Project Table.
from schrodinger import maestro
from schrodinger import project
pt = maestro.project_table_get()
for row in pt.selected_rows:
st = row.getStructure()
# .. compute new value 'new_value' for this structure here ...
row['r_user_New_Property'] = new_value
pt.update()
There are three ways to iterate over entries in the Project Table:
from schrodinger import maestro
from schrodinger import project
pt = maestro.project_table_get()
# To iterate over all selected rows, use:
for row in pt.selected_rows:
pass
# To iterate over all entries included in the workspace, use:
for row in pt.included_rows:
pass
# To iterate over all rows:
for row in pt.all_rows:
pass
# To get a list of schrodinger.structure.Structure objects (with properties) from the Project Table
# entries included in the workspace:
included_structures = []
for row in pt.included_rows:
included_structures.append(row.getStructure())
Selecting entries in the project table may be performed by a number of
different mechanisms. Maestro has built-in support for selecting entries with
entryselectonly
and similar commands. This support is provided by both the
Entry Selection dialog box and the ability to define filters (see the
Maestro online help and the Maestro User Manual for complete descriptions
of these features).
However, there are limits to what type of selections can be generated with these features. They all rely on Maestro’s Entry Selection Language (ESL). The ESL was designed to work on the properties associated with entries. While it is possible to create expressions of arbitrary complexity with the ESL, it is not possible to make selections based on calculations performed on the actual Structure object (number of atoms etc.) nor to make selections based on functions of the entry properties (such as the difference between two properties). To set the selected state of a given entry the is_selected property can be used:
from schrodinger import maestro
from schrodinger import project
pt = maestro.project_table_get()
for row in pt.all_rows:
if row['r_some_prop'] - row['r_some_other_prop'] > 0.001:
row.is_selected = True
else:
row.is_selected = False
It’s also possible to add any number of new columns to the project table from a Python script. Simply reference new properties of any row:
from schrodinger.maestro import maestro
from schrodinger import project
pt = maestro.project_table_get()
for row in pt.all_rows:
st = row.structure
num_h_atoms = 0
num_c_atoms = 0
for a in st.atom:
if a.atomic_number == 1:
num_h_atoms += 1
elif a.atomic_number == 6:
num_c_atoms += 1
# Add or overwrite if already present
# Format for name is: <type>_<author>_<property_name>
# type can be i (integer), r (real), b (boolean), s (string)
# author is "m" for maestro, "user" for user, etc.
# property_name is any text. Underscores are allowed.
row['i_user_Num_Carbons'] = num_c_atoms
row['i_user_Num_Hydrogens'] = num_h_atoms
pt.refreshTable()
Registering Functions to be Called By Maestro¶
Normally, the Python functions you write for use in Maestro will be called
explicitly with the pythonrun
command. However, there are some
situations in which you may want to supply a Python function that will be
called when particular events occur during the normal operation of Maestro.
These callback functions are a powerful way to extend and modify the default
behavior of Maestro.
To support atom picking in the Workspace use maestro.picking_atom_start() and pass a function to be called when an atom is picked in the Workspace. The function you pass should be expecting a single parameter that is the atom number of the atom picked in the Workspace.
If you need to perform an action periodically, you can use maestro.periodic_callback_add():
maestro.periodic_callback_add(your_callback_function)
to register a Python function that will be called approximately 20 times a second. If you would like to perform the action less frequently, maintain a counter in your function and only take action every nth time.
You can also register a hover callback function, using maestro.hover_callback_add():
maestro.hover_callback_add(your_callback_function)
The callback will be called by Maestro whenever the pointer is paused (“hovers”) over an atom in the Workspace. When you no longer need the mouse hover callback, unregister your function using:
maestro.hover_callback_remove(your_callback_function_name)
You can add a Python function that will be called each time the Workspace is drawn using maestro.workspace_draw_function_add(). With this callback, just about anything that can be rendered with OpenGL can be drawn in the Workspace. NOTE: This API was removed in 2020-1.
It is also possible to register a callback that will be called when the contents of the Workspace are modified with maestro.workspace_changed_function_add():
maestro.workspace_changed_function_add(your_callback_function)
The callback function in this case is called with a string parameter that indicates exactly what has been modified in the workspace. It will be one of:
everything
color
geometry
visibility
representation
properties
coordinates
connectivity
unknown
In this way a script that does not care about, say, changes in the representation of the Structure object in the Workspace can choose to take no action in these cases.
Note
The state of the Project table is not well-defined during the “workspace changed” callback, and so you can’t reliably check on the inclusion state of an entry. Please use the project_update_callback_add function for this purpose. Also note that you should avoid calling maestro commands during these callbacks in order to maintain consistent Maestro internal state.
Displaying a GUI Within Maestro Using Python¶
We support (and use) the PyQt* framework, a Python wrapping for the Qt GUI library. PyQt also makes provisions for third party development; for more information please see Riverbank’s website and the PyQt FAQS.
*Any links to third party software or information available on this website are provided “as is” without warranty of any kind, either expressed or implied and such software is to be used at your own risk and you are solely responsible for compliance with any applicable third party licensing requirements. Furthermore, at any time without prior notice, we may make changes to the links pointing to third party software or documentation made available on the third party’s website and we do not guarantee the availability of any third party’s website, software or information.
Other Maestro Interactions¶
There are a number of other useful functions in the maestro.py module.