Running a Background Process

Chimera may be used in conjunction with command line programs, e.g., Modeller, by creating the required input files, executing the program, and importing the generated results, e.g., as attributes of molecules.

Example RunSubprocess.py

Class CountAtoms assigns two attributes, "numAtoms" and "numHetatms", to a molecule by exporting the molecule as a PDB file and running the "grep" program twice. The "grep" invocations are run in the background so that Chimera stays interactive while they execute.
class CountAtoms:

The constructor sets up a temporary file for the PDB output, and a Chimera task instance for showing progress to the user.
def __init__(self, m, grepPath):

Generate a temporary file name for PDB file. We use Chimera's osTemporaryFile function because it automatically deletes the file when Chimera exits.
import OpenSave
self.pdbFile = OpenSave.osTemporaryFile(suffix=".pdb", prefix="rg")
self.outFile = OpenSave.osTemporaryFile(suffix=".out", prefix="rg")

Write molecule in to temporary file in PDB format.
self.molecule = m
import Midas
Midas.write([m], None, self.pdbFile)

Set up a task instance for showing user our status.
from chimera import tasks
self.task = tasks.Task("atom count for %s" % m.name, self.cancelCB)

Start by counting the ATOM records first.
self.countAtoms()

cancelCB is called when user cancels via the task panel
def cancelCB(self):

self.molecule = None

countAtoms uses "grep" to count the number of ATOM records.
def countAtoms(self):

from chimera import SubprocessMonitor as SM
self.outF = open(self.outFile, "w")
self.subproc = SM.Popen([ grepPath, "-c", "^ATOM", self.pdbFile ], stdout=self.outF)
SM.monitor("count ATOMs", self.subproc, task=self.task, afterCB=self._countAtomsCB)

_countAtomsCB is the callback invoked when the subprocess started by countAtoms completes.
def _countAtomsCB(self, aborted):

Always close the open file created earlier
self.outF.close()

If user canceled the task, do not continue processing.
if aborted or self.molecule is None:

self.finished()
return

Make sure the process exited normally.
if self.subproc.returncode != 0 and self.subproc.returncode != 1:

self.task.updateStatus("ATOM count failed")
self.finished()
return

Process exited normally, so the count is in the output file. The error checking code (in case the output is not a number) is omitted to keep this example simple.
f = open(self.outFile)
data = f.read()
f.close()
self.molecule.numAtoms = int(data)

Start counting the HETATM records
self.countHetatms()

countHetatms uses "grep" to count the number of HETATM records.
def countHetatms(self):

from chimera import SubprocessMonitor as SM
self.outF = open(self.outFile, "w")
self.subproc = SM.Popen([ grepPath, "-c", "^HETATM", self.pdbFile ], stdout=self.outF)
SM.monitor("count HETATMs", self.subproc, task=self.task, afterCB=self._countHetatmsCB)

_countHetatmsCB is the callback invoked when the subprocess started by countHetatms completes.
def _countHetatmsCB(self, aborted):

Always close the open file created earlier
self.outF.close()

If user canceled the task, do not continue processing.
if aborted or self.molecule is None:

self.finished()
return

Make sure the process exited normally.
if self.subproc.returncode != 0 and self.subproc.returncode != 1:

self.task.updateStatus("HETATM count failed")
self.finished()
return

Process exited normally, so the count is in the output file. The error checking code (in case the output is not a number) is omitted to keep this example simple.
f = open(self.outFile)
data = f.read()
f.close()
self.molecule.numHetatms = int(data)

No more processing needs to be done.
self.finished()

finished is called to clean house.
def finished(self):

Temporary files will be removed when Chimera exits, but may be removed here to minimize their lifetime on disk. The task instance must be notified so that it is labeled completed in the task panel.
self.task.finished()

Set instance variables to None to release references.
self.task = None
self.molecule = None
self.subproc = None

Below is the main program. First, we find the path to the "grep" program. Then, we run CountAtoms for each molecule.
from CGLutil import findExecutable
grepPath = findExecutable.findExecutable("grep")
if grepPath is None:

from chimera import NonChimeraError
raise NonChimeraError("Cannot find path to grep")

Add "numAtoms" and "numHetatms" attributes to all open molecules.
import chimera
from chimera import Molecule
for m in chimera.openModels.list(modelTypes=[Molecule]):

CountAtoms(m, grepPath)

Running the Example

You can execute the example code by downloading the linked Python script and opening it with the File→Open menu item or with the open command. Note that the .py extension is required for the open dialog/command to recognize that the file is a Python script.