Just as menu commands are added in an extension's ChimeraExtension.py file, so are typed commands. This is for two reasons:
Typed commands are added using Midas.midas_text's
addCommand
function.
addCommand
has two mandatory arguments:
~
command is typed. If
omitted, ~
command will raise an error.True
. If help is a string, it is
interpreted as an URL that will be brought up in a browser to
display help. If help is a tuple, it should be a
(path, package) 2-tuple, where path
specifies a file relative to package's helpdir
subdirectory.
The file will be displayed in a browser as help. Note that
though package can be an actual imported package, importing the package
would defeat the purpose of avoiding importing the module early,
so package can just be a string specifying the module
name instead.
If the help keyword is omitted, no help will be
provided.The callback function you register with addCommand
will
be invoked with two arguments:
addCommand
.The parsing of the typed arguments and calling of the function that
actually performs the command work is typically handled through
Midas.midas_text's doExtensionFunc
function.
This is discussed in detail below in The doExtensionFunc
Function.
Before getting into that, we know enough at this point that we can
look at a brief example.
def cmdAddAttr(cmdName, args): from AddAttr import addAttributes from Midas.midas_text import doExtensionFunc doExtensionFunc(addAttributes, args, specInfo=[("spec", "models", "molecules")]) from Midas.midas_text import addCommand addCommand("defattr", cmdAddAttr, help=True)
First, a callback function named cmdAddAttr
is defined
that will later be registered with addCommand
. The callback
imports a "workhorse" function (addAttributes
)
from the main module and doExtensionFunc
from
Midas.midas_text and then calls doExtensionFunc
to process the typed arguments and call addAttributes
appropriately.
Note that addAttributes
is imported inside
the cmdAddAttr
definition. If it were outside, then the
whole module would be imported during Chimera startup, which we are
trying to avoid.
After the cmdAddAttr
function is defined,
Midas.midas_text's addCommand
is called to add the
defattr command to the command interpreter.
Since the help for the defattr command is shipped with Chimera,
the help keyword argument is set to True
.
As seen in the An Example section above,
doExtensionFunc
has two mandatory arguments:
the "workhorse" function that actually carries out
the operation requested by the user, and a string containing the
command arguments that the user typed.
doExtensionFunc
introspects the workhorse function
to determine how many mandatory arguments it expects and what
keyword arguments it accepts. The initial arguments in the typed
string are assumed to correspond to the mandatory arguments, and
the remainder of the typed string is assumed to specify valid
keyword/value pairs (space separated rather than "=" separated).
Keywords will be matched regardless of case, and the user need only
type enough characters to distinguish keywords.
doExtensionFunc
has two keyword arguments:
doExtensionFunc
will behave as if
the workhorse function did not have these keywords.Atom
s, Residue
s,
etc., for which the user needs to type an atom specifier,
that information is given here. specInfo is a list
of 3-tuples.
The first component of the 3-tuple is the keyword the user
should type or, if this is a positional argument, the name that
the argument should be assumed to have for type-guessing purposes
(in either case it needs to end in "spec"). The next
component is the real argument name that the function uses (it will
automatically be added to invalid). The final
component is the method name to apply to the selection generated
by the atom spec in order to extract the desired list (typically
"atoms", "residues", "molecules",
or "models"). If the method name is None, then the
selection itself will be returned.Typed arguments are processed using some heuristic rules to convert them to their most "natural" type. However, the argument name used by the workhorse function can influence how the typed argument is processed. In particular, if the argument name (ignoring case) ends in...
MaterialColor
.Selection
.Furthermore, if the user provides a keyword argument multiple times, the value provided to the workhorse function will be a list of the individual values.
In some cases it may be desirable to provide a "shim"
function between the doExtensionFunc
"workhorse"
function and the module's true workhorse function in order to provide
more user-friendly argument names or default values
than those of the normal module API.
If you want to have errors from your command-line function handled the
same was as other command-line errors (i.e. shown as red text
in the status line rather than raising an error dialog), then you need to
have the function you register with addCommand
raise
MidasError in those cases instead of other error types.
This may involve
embedding your use of doExtensionFunc
in a try
/
except
block and re-raising caught errors as MidasError.
MidasError is defined in the Midas module.
The initial code is the same as for the ToolbarButtonExtension example
Here we define two functions, one to handle the "mainchain" command,
and one to handle the "~mainchain" command.
Import the module's workhorse function.
It is imported inside the function definition so that
it does not slow down Chimera startup with extra imports
in the main scope.
Import and use the Midas.midas_text doExtensionFunc procedure
to process typed arguments and call the mainchain() function
appropriately. For a simple function like mainchain(), which
takes no arguments, using doExtensionFunc is probably overkill.
One could instead use the approach applied in the revMainchainCmd
function below and simply test for the presence of any
arguments (raising MidasError if any are found) and directly calling
the mainchain() function otherwise. As implemented here, using
doExtensionFunc, if the user does provide arguments then
doExtensionFunc will raise an error complaining that there
were unknown keyword arguments supplied.
The function for "~mainchain"
We are going to implement ~mainchain as a synonym for "display",
so we import runCommand which simplifies doing that.
Raising MidasError will cause the error message
to show up in the status line as red text
runCommand takes any legal command-line command and executes it.
Now actually register the "mainchain" command with the command interpreter
by using addCommand(). The first argument is the command name and the
second is the callback function for doing the work. The The example files
(ChimeraExtension.py,
__init__.py,
and ToolbarButton.tiff)
must be saved in a directory named
import chimera.extension
class MainChainEMO(chimera.extension.EMO):
def name(self):
return 'Backbone'
def description(self):
return 'display only protein backbone'
def categories(self):
return ['Utilities']
def icon(self):
return self.path('mainchain.tiff')
def activate(self):
self.module().mainchain()
chimera.extension.manager.registerExtension(MainChainEMO(__file__))
def mainchainCmd(cmdName, args):
from ToolbarButtonCommand import mainchain
from Midas.midas_text import doExtensionFunc
doExtensionFunc(mainchain, args)
def revMainchainCmd(cmdName, args):
from chimera import runCommand
from Midas import MidasError
if args:
raise MidasError("~mainchain takes no arguments")
runCommand("display")
revFunc
keyword
specifies the function to implement "~mainchain". The help
keyword has
been omitted, therefore no help will be provided.
from Midas.midas_text import addCommand
addCommand("mainchain", mainchainCmd, revFunc=revMainchainCmd)
Running the Example
ToolbarButtonCommand
.
To run the example, start chimera,
bring up the Tools preference category (via
the Preferences entry in the Favorites menu;
then choosing the "Tools" preference category), use
the Add button to add the directory above the
ToolbarButtonCommand
directory.
You should then be able to type "mainchain" to the Chimera
command line (start the command line from the Favorites menu if necessary).