예제 #1
0
 def run(self):
     if not self.saveinput():
         return
     env = os.environ.copy()
     env['OMMPROTOCOL_SLAVE'] = '1'
     env['PYTHONIOENCODING'] = 'latin-1'
     self.task = Task("OMMProtocol for {}".format(self.filename),
                      cancelCB=self._clear_cb,
                      statusFreq=((1, ), 1))
     self.subprocess = Popen(
         ['ommprotocol', self.filename],
         stdout=PIPE,
         stderr=PIPE,
         progressCB=self._progress_cb,  #universal_newlines=True,
         bufsize=1,
         env=env)
     self.progress = SubprocessTask("OMMProtocol",
                                    self.subprocess,
                                    task=self.task,
                                    afterCB=self._after_cb)
     self.task.updateStatus("Running OMMProtocol")
     self.queue = LifoQueue()
     thread = Thread(target=enqueue_output,
                     args=(self.subprocess.stdout, self.queue))
     thread.daemon = True  # thread dies with the program
     thread.start()
     self.ensemble = _TrajProxy()
     self.molecule = self.ensemble.molecule = self.gui.ui_chimera_models.getvalue(
     )
     self.ensemble.name = 'Trajectory for {}'.format(self.molecule.name)
     self.ensemble.startFrame = self.ensemble.endFrame = 1
     self.movie_dialog = MovieDialog(self.ensemble, externalEnsemble=True)
     self.gui.Close()
예제 #2
0
    def _initBlast(self, program, db, queryName, querySeq, evalue, matrix,
                   passes):
        self.program = program
        self.db = db
        self.queryName = queryName
        self.params = (querySeq, evalue, matrix, passes)
        from WebServices.AppService_types import ns0
        from WebServices.opal_client import OpalService
        inputFile = ns0.InputFileType_Def("inputFile")
        inputFile._name = "query.fa"
        inputFile._contents = self.makeFasta(queryName, querySeq)
        service = "BlastproteinServicePort"
        argList = "-P %s -d %s -i %s -o %s -e %s -M %s -p %s" % (
            program, db, inputFile._name, self.outputFile, evalue, matrix,
            passes)
        try:
            self.opal = OpalService(service)
        except:
            import traceback, sys
            print "Traceback from Blast request:"
            traceback.print_exc(file=sys.stdout)
            print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
            from chimera import NonChimeraError
            raise NonChimeraError("Blast web service appears "
                                  "to be down.  See Reply Log "
                                  "for more details.")
        self.opal.launchJob(argList, _inputFile=[inputFile])
        from chimera.tasks import Task
        self.task = Task(self._title(), self.cancelCB, self.statusCB)
예제 #3
0
    def run(self, *xyz, **options):
        """
        Launch a NCIPlot essay for specified xyz files and options.
        Read on self.create_nci_method documentation for further info.

        Notes
        -----
        This function uses threads, queues, tasks and subprocesses, so it 
        can be a bit difficult to understand for newcomers.

        `subprocess` is used to launch an external program (nciplot, in this
        case), and `task` is a Chimera feature to monitor that process in the
        tasks panels. It also allows to print status updates to the bottom bar.
        `task` and `subprocess` are synced with Chimera's `monitor`, which
        includes a very useful `afterCB` parameter: a callable that will be
        called when the process ends.

        If we needed to get it in realtime, we should add queues and threads.
        As a workaround, we need to get the stdout to an async queue (in
        realtime!) with a separate thread. 
        >>> from threading import Thread
        >>> from Queue import Queue
        >>> def enqueue_output(out, queue):
                for line in iter(out.readline, b''):
                    queue.put(line)
                out.close()
        >>> self.queue = Queue()
        >>> thread = Thread(target=enqueue_output, args=(self.subprocess.stdout, self.queue))
        >>> thread.daemon = True  # thread dies with the program
        >>> thread.start()

        Then, we can read the stdout from the queue:

        >>> self.queue.put(None) # Sentinel value. When iter gets this, it stops.
        >>> for line in iter(self.queue.get, None):
        >>>     # do stuff

        Check this SO answer for more info:
        
        http://stackoverflow.com/questions/375427/
        non-blocking-read-on-a-subprocess-pipe-in-python/4896288#4896288
        
        """
        nci_file = osTemporaryFile(suffix='.nci')
        oldworkingdir = os.getcwd()
        tmpdir, tmpfile = os.path.split(nci_file)
        if 'name' not in options or options['name'] is None:
            options['name'] = tmpfile[:-4]
        nci_input = self.create_nci_input(xyz, **options)
        os.chdir(tmpdir)
        with open(nci_file, 'w') as f:
            f.write(nci_input.read())
        name = ', '.join(os.path.basename(f) for f in xyz)
        self._tmpdir = tmpdir
        self.task = Task("NCIPlot for {}".format(name), cancelCB=self._clear_task)
        self.subprocess = Popen([self.binary, nci_file], stdout=PIPE, progressCB= lambda p: 0)
        monitor("NCIPlot", self.subprocess, task=self.task, afterCB=self._after_cb)
        self.task.updateStatus("Running NCIPlot")
        os.chdir(oldworkingdir)
예제 #4
0
 def _initSession(self, program, db, queryName, params, running, opalData):
     self.program = program
     self.db = db
     self.queryName = queryName
     self.params = params
     from WebServices.opal_client import OpalService
     self.opal = OpalService(sessionData=opalData)
     if not running:
         self.task = None
     else:
         from chimera.tasks import Task
         self.task = Task(self._title(), self.cancelCB, self.statusCB)
예제 #5
0
 def __init__(self, queryName, querySeq, evalue, matrix, finishCB):
     from WebServices.AppService_types import ns0
     from WebServices.opal_client import OpalService
     self.queryName = queryName
     inputFile = ns0.InputFileType_Def("inputFile")
     inputFile._name = "query.fa"
     inputFile._contents = self.makeFasta(queryName, querySeq)
     service = "BlastpdbServicePort"
     argList = "-i %s -o %s -e %s -M %s" % (self.inputFile._name,
                                            self.outputFile, evalue, matrix)
     self.opal = OpalService(service)
     self.opal.launchJob(argList, _inputFile=[inputFile])
     self.finishCB = finishCB
     from chimera.tasks import Task
     self.task = Task("blastpdb %s" % queryName, self.cancelCB,
                      self.statusCB)
예제 #6
0
	def _initBlast(self, program, db, queryName, querySeq,
			evalue, matrix, passes):
		self.program = program
		self.db = db
		self.queryName = queryName
		self.params = (querySeq, evalue, matrix, passes)
		from WebServices.AppService_types import ns0
		from WebServices.opal_client import OpalService
		inputFile = ns0.InputFileType_Def("inputFile")
		inputFile._name = "query.fa"
		inputFile._contents = self.makeFasta(queryName, querySeq)
		service = "BlastproteinServicePort"
		argList = "-P %s -d %s -i %s -o %s -e %s -M %s -p %s" % (
				program, db, inputFile._name,
				self.outputFile, evalue, matrix, passes)
		try:
			self.opal = OpalService(service)
		except:
			import traceback, sys
			print "Traceback from Blast request:"
			traceback.print_exc(file=sys.stdout)
			print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
			from chimera import NonChimeraError
			raise NonChimeraError("Blast web service appears "
						"to be down.  See Reply Log "
						"for more details.")
		self.opal.launchJob(argList, _inputFile=[inputFile])
		from chimera.tasks import Task
		self.task = Task(self._title(), self.cancelCB,
								self.statusCB)
예제 #7
0
    def __init__(self, pdbpath, expath, name, fig):
        from WebServices.AppService_types import ns0
        from WebServices.opal_client import OpalService
        self.pdbpath = pdbpath
        self.expath = expath
        self.name = name
        self.figure = fig

        pdbFile = ns0.InputFileType_Def("inputFile")
        pdbFile._name = "pdbfile"
        f = open(pdbpath)
        pdbFile._contents = f.read()
        f.close()
        files = [pdbFile]
        argList = "pdbfile"
        if expath:
            exFile = ns0.InputFileType_Def("inputFile")
            exFile._name = "exfile"
            f = open(expath)
            exFile._contents = f.read()
            f.close()
            files.append(exFile)
            argList += " exfile"

        try:
            self.opal = OpalService("SAXSService")
        except:
            import traceback, sys
            print "Traceback from SAXS request:"
            traceback.print_exc(file=sys.stdout)
            print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
            from chimera import NonChimeraError
            raise NonChimeraError("SAXS web service appears "
                                  "to be down.  See Reply Log "
                                  "for more details.")

        self.opal.launchJob(argList, _inputFile=files)
        from chimera.tasks import Task
        self.task = Task("SAXS " + self.name, self.cancelCB, self.statusCB)
예제 #8
0
	def _initSession(self, program, db, queryName,
				params, running, opalData):
		self.program = program
		self.db = db
		self.queryName = queryName
		self.params = params
		from WebServices.opal_client import OpalService
		self.opal = OpalService(sessionData=opalData)
		if not running:
			self.task = None
		else:
			from chimera.tasks import Task
			self.task = Task(self._title(), self.cancelCB,
								self.statusCB)
예제 #9
0
	def __init__(self, queryName, querySeq, evalue, matrix, finishCB):
		from WebServices.AppService_types import ns0
		from WebServices.opal_client import OpalService
		self.queryName = queryName
		inputFile = ns0.InputFileType_Def("inputFile")
		inputFile._name = "query.fa"
		inputFile._contents = self.makeFasta(queryName, querySeq)
		service = "BlastpdbServicePort"
		argList = "-i %s -o %s -e %s -M %s" % (
				self.inputFile._name, self.outputFile,
				evalue, matrix)
		self.opal = OpalService(service)
		self.opal.launchJob(argList, _inputFile=[inputFile])
		self.finishCB = finishCB
		from chimera.tasks import Task
		self.task = Task("blastpdb %s" % queryName, self.cancelCB,
								self.statusCB)
예제 #10
0
  def __init__(self, pdbpath, expath, name, fig):
    from WebServices.AppService_types import ns0
    from WebServices.opal_client import OpalService
    self.pdbpath = pdbpath
    self.expath = expath
    self.name = name
    self.figure = fig

    pdbFile = ns0.InputFileType_Def("inputFile")
    pdbFile._name = "pdbfile"
    f = open(pdbpath)
    pdbFile._contents = f.read()
    f.close()
    files = [ pdbFile ]
    argList = "pdbfile"
    if expath:
      exFile = ns0.InputFileType_Def("inputFile")
      exFile._name = "exfile"
      f = open(expath)
      exFile._contents = f.read()
      f.close()
      files.append(exFile)
      argList += " exfile"
    
    try:
      self.opal = OpalService("SAXSService")
    except:
      import traceback, sys
      print "Traceback from SAXS request:"
      traceback.print_exc(file=sys.stdout)
      print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
      from chimera import NonChimeraError
      raise NonChimeraError("SAXS web service appears "
            "to be down.  See Reply Log "
            "for more details.")

    self.opal.launchJob(argList, _inputFile=files)
    from chimera.tasks import Task
    self.task = Task("SAXS " + self.name, self.cancelCB, self.statusCB)
예제 #11
0
class BlastpdbService:

    outputFile = "blast.out"

    def __init__(self, queryName, querySeq, evalue, matrix, finishCB):
        from WebServices.AppService_types import ns0
        from WebServices.opal_client import OpalService
        self.queryName = queryName
        inputFile = ns0.InputFileType_Def("inputFile")
        inputFile._name = "query.fa"
        inputFile._contents = self.makeFasta(queryName, querySeq)
        service = "BlastpdbServicePort"
        argList = "-i %s -o %s -e %s -M %s" % (self.inputFile._name,
                                               self.outputFile, evalue, matrix)
        self.opal = OpalService(service)
        self.opal.launchJob(argList, _inputFile=[inputFile])
        self.finishCB = finishCB
        from chimera.tasks import Task
        self.task = Task("blastpdb %s" % queryName, self.cancelCB,
                         self.statusCB)

    def cancelCB(self):
        self.task = None

    def statusCB(self):
        self.task.updateStatus(self.opal.currentStatus())
        if not self.opal.isFinished():
            self.opal.queryStatus()
            return
        self.task = None
        fileMap = self.opal.getOutputs()
        if self.opal.isFinished() > 0:
            # Successful completion
            self.finishCB(self.getURLContent(fileMap[self.outputFile]))
        else:
            # Failed
            from chimera import replyobj
            replyobj.error("blastpdb %s failed; "
                           "see Reply Log for more information" %
                           self.queryName)
            try:
                self.showURLContent("blastpdb stdout", fileMap["stdout"])
            except KeyError:
                pass
            try:
                self.showURLContent("blastpdb stderr", fileMap["stderr"])
            except KeyError:
                pass

    def makeFasta(self, name, seq):
        output = [">QUERY\n"]
        maxLine = 60
        for i in range(0, len(seq), maxLine):
            end = min(i + maxLine, len(seq))
            output.append("%s\n" % seq[i:end])
        return ''.join(output)

    def getURLContent(self, url):
        import urllib2
        f = urllib2.urlopen(url)
        data = f.read()
        f.close()
        return data

    def showURLContent(self, title, url):
        from chimera import replyobj
        data = self.getURLContent(url)
        replyobj.message("%s\n-----\n%s-----\n" % (title, data))
예제 #12
0
class BlastproteinService:

	outputFile = "blast.out"

	def __init__(self, finishCB, params=None, sessionData=None):
		self.finishCB = finishCB
		if params is not None:
			self._initBlast(*params)
		else:
			self._initSession(*sessionData)

	def _initBlast(self, program, db, queryName, querySeq,
			evalue, matrix, passes):
		self.program = program
		self.db = db
		self.queryName = queryName
		self.params = (querySeq, evalue, matrix, passes)
		from WebServices.AppService_types import ns0
		from WebServices.opal_client import OpalService
		inputFile = ns0.InputFileType_Def("inputFile")
		inputFile._name = "query.fa"
		inputFile._contents = self.makeFasta(queryName, querySeq)
		service = "BlastproteinServicePort"
		argList = "-P %s -d %s -i %s -o %s -e %s -M %s -p %s" % (
				program, db, inputFile._name,
				self.outputFile, evalue, matrix, passes)
		try:
			self.opal = OpalService(service)
		except:
			import traceback, sys
			print "Traceback from Blast request:"
			traceback.print_exc(file=sys.stdout)
			print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
			from chimera import NonChimeraError
			raise NonChimeraError("Blast web service appears "
						"to be down.  See Reply Log "
						"for more details.")
		self.opal.launchJob(argList, _inputFile=[inputFile])
		from chimera.tasks import Task
		self.task = Task(self._title(), self.cancelCB,
								self.statusCB)

	def _initSession(self, program, db, queryName,
				params, running, opalData):
		self.program = program
		self.db = db
		self.queryName = queryName
		self.params = params
		from WebServices.opal_client import OpalService
		self.opal = OpalService(sessionData=opalData)
		if not running:
			self.task = None
		else:
			from chimera.tasks import Task
			self.task = Task(self._title(), self.cancelCB,
								self.statusCB)

	def _title(self):
		return "%s %s: %s" % (self.program, self.db, self.queryName)

	def sessionData(self):
		return (self.program, self.db, self.queryName, self.params,
				self.task is not None,
				self.opal.sessionData())

	def cancelCB(self):
		self.task = None

	def statusCB(self):
		self.task.updateStatus(self.opal.currentStatus())
		if not self.opal.isFinished():
			self.opal.queryStatus()
			return
		self.task = None
		fileMap = self.opal.getOutputs()
		if self.opal.isFinished() > 0:
			# Successful completion
			self.finishCB(self.getURLContent(
						fileMap[self.outputFile]))
		else:
			# Failed
			from chimera import replyobj
			replyobj.error("blast %s failed; "
					"see Reply Log for more information\n"
					% self.queryName)
			self.showURLContent("blast stderr", fileMap["stderr.txt"])
			self.showURLContent("blast stdout", fileMap["stdout.txt"])

	def makeFasta(self, name, seq):
		output = [ ">QUERY\n" ]
		maxLine = 60
		for i in range(0, len(seq), maxLine):
			end = min(i + maxLine, len(seq))
			output.append("%s\n" % seq[i:end])
		return ''.join(output)

	def getURLContent(self, url):
		import urllib2
		f = urllib2.urlopen(url)
		data = f.read()
		f.close()
		return data

	def showURLContent(self, title, url):
		from chimera import replyobj
		data = self.getURLContent(url)
		replyobj.message("%s\n-----\n%s-----\n" % (title, data))
예제 #13
0
class BlastproteinService:

    outputFile = "blast.out"

    def __init__(self, finishCB, params=None, sessionData=None):
        self.finishCB = finishCB
        if params is not None:
            self._initBlast(*params)
        else:
            self._initSession(*sessionData)

    def _initBlast(self, program, db, queryName, querySeq, evalue, matrix,
                   passes):
        self.program = program
        self.db = db
        self.queryName = queryName
        self.params = (querySeq, evalue, matrix, passes)
        from WebServices.AppService_types import ns0
        from WebServices.opal_client import OpalService
        inputFile = ns0.InputFileType_Def("inputFile")
        inputFile._name = "query.fa"
        inputFile._contents = self.makeFasta(queryName, querySeq)
        service = "BlastproteinServicePort"
        argList = "-P %s -d %s -i %s -o %s -e %s -M %s -p %s" % (
            program, db, inputFile._name, self.outputFile, evalue, matrix,
            passes)
        try:
            self.opal = OpalService(service)
        except:
            import traceback, sys
            print "Traceback from Blast request:"
            traceback.print_exc(file=sys.stdout)
            print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
            from chimera import NonChimeraError
            raise NonChimeraError("Blast web service appears "
                                  "to be down.  See Reply Log "
                                  "for more details.")
        self.opal.launchJob(argList, _inputFile=[inputFile])
        from chimera.tasks import Task
        self.task = Task(self._title(), self.cancelCB, self.statusCB)

    def _initSession(self, program, db, queryName, params, running, opalData):
        self.program = program
        self.db = db
        self.queryName = queryName
        self.params = params
        from WebServices.opal_client import OpalService
        self.opal = OpalService(sessionData=opalData)
        if not running:
            self.task = None
        else:
            from chimera.tasks import Task
            self.task = Task(self._title(), self.cancelCB, self.statusCB)

    def _title(self):
        return "%s %s: %s" % (self.program, self.db, self.queryName)

    def sessionData(self):
        return (self.program, self.db, self.queryName, self.params, self.task
                is not None, self.opal.sessionData())

    def cancelCB(self):
        self.task = None

    def statusCB(self):
        self.task.updateStatus(self.opal.currentStatus())
        if not self.opal.isFinished():
            self.opal.queryStatus()
            return
        self.task = None
        fileMap = self.opal.getOutputs()
        if self.opal.isFinished() > 0:
            # Successful completion
            self.finishCB(self.getURLContent(fileMap[self.outputFile]))
        else:
            # Failed
            from chimera import replyobj
            replyobj.error("blast %s failed; "
                           "see Reply Log for more information\n" %
                           self.queryName)
            self.showURLContent("blast stderr", fileMap["stderr.txt"])
            self.showURLContent("blast stdout", fileMap["stdout.txt"])

    def makeFasta(self, name, seq):
        output = [">QUERY\n"]
        maxLine = 60
        for i in range(0, len(seq), maxLine):
            end = min(i + maxLine, len(seq))
            output.append("%s\n" % seq[i:end])
        return ''.join(output)

    def getURLContent(self, url):
        import urllib2
        f = urllib2.urlopen(url)
        data = f.read()
        f.close()
        return data

    def showURLContent(self, title, url):
        from chimera import replyobj
        data = self.getURLContent(url)
        replyobj.message("%s\n-----\n%s-----\n" % (title, data))
예제 #14
0
class NCIPlot(object):

    """
    A wrapper around NCIPlot binary interface
    """

    def __init__(self, binary, dat_directory, success_callback=None, clear_callback=None):
        self._check_paths(binary, dat_directory)
        self.binary = binary
        self.dat_directory = dat_directory
        os.environ['NCIPLOT_HOME'] = os.path.dirname(self.dat_directory)
        self.success_callback = success_callback
        self.clear_callback = clear_callback
        self.task = None
        self.subprocess = None
        self.stdout = None
        self.queue = None
        self.implementation = 'CUDA' if 'cuda' in os.path.split(self.binary)[1].lower() \
                                     else 'CPU'
        self.parse_stdout = self._parse_stdout_cuda if self.implementation == 'CUDA' \
                                                    else self._parse_stdout_cpu

    def run(self, *xyz, **options):
        """
        Launch a NCIPlot essay for specified xyz files and options.
        Read on self.create_nci_method documentation for further info.

        Notes
        -----
        This function uses threads, queues, tasks and subprocesses, so it 
        can be a bit difficult to understand for newcomers.

        `subprocess` is used to launch an external program (nciplot, in this
        case), and `task` is a Chimera feature to monitor that process in the
        tasks panels. It also allows to print status updates to the bottom bar.
        `task` and `subprocess` are synced with Chimera's `monitor`, which
        includes a very useful `afterCB` parameter: a callable that will be
        called when the process ends.

        If we needed to get it in realtime, we should add queues and threads.
        As a workaround, we need to get the stdout to an async queue (in
        realtime!) with a separate thread. 
        >>> from threading import Thread
        >>> from Queue import Queue
        >>> def enqueue_output(out, queue):
                for line in iter(out.readline, b''):
                    queue.put(line)
                out.close()
        >>> self.queue = Queue()
        >>> thread = Thread(target=enqueue_output, args=(self.subprocess.stdout, self.queue))
        >>> thread.daemon = True  # thread dies with the program
        >>> thread.start()

        Then, we can read the stdout from the queue:

        >>> self.queue.put(None) # Sentinel value. When iter gets this, it stops.
        >>> for line in iter(self.queue.get, None):
        >>>     # do stuff

        Check this SO answer for more info:
        
        http://stackoverflow.com/questions/375427/
        non-blocking-read-on-a-subprocess-pipe-in-python/4896288#4896288
        
        """
        nci_file = osTemporaryFile(suffix='.nci')
        oldworkingdir = os.getcwd()
        tmpdir, tmpfile = os.path.split(nci_file)
        if 'name' not in options or options['name'] is None:
            options['name'] = tmpfile[:-4]
        nci_input = self.create_nci_input(xyz, **options)
        os.chdir(tmpdir)
        with open(nci_file, 'w') as f:
            f.write(nci_input.read())
        name = ', '.join(os.path.basename(f) for f in xyz)
        self._tmpdir = tmpdir
        self.task = Task("NCIPlot for {}".format(name), cancelCB=self._clear_task)
        self.subprocess = Popen([self.binary, nci_file], stdout=PIPE, progressCB= lambda p: 0)
        monitor("NCIPlot", self.subprocess, task=self.task, afterCB=self._after_cb)
        self.task.updateStatus("Running NCIPlot")
        os.chdir(oldworkingdir)

    def _after_cb(self, aborted):
        """
        Called after the subprocess ends.
        """
        if aborted:
            self._clear_task()
            self.clear_callback()
            return
        if self.subprocess.returncode != 0:
            self.task.updateStatus("NCIPlot calculation failed!")
            self._clear_task()
            self.clear_callback()
            return
        self.task.updateStatus("Parsing NCIPlot output")
        data = self.parse_stdout(self.subprocess.stdout)
        self.task.updateStatus("Loading volumes")
        self.success_callback(data)
        self.task.updateStatus("Done!")
        self._clear_task()

    def _clear_task(self):
        """
        House cleaning 
        """
        self.task.finished()
        self.subprocess.stdout.close()
        self.task, self.subprocess, self.queue, self._tmpdir = None, None, None, None

    def _parse_stdout_cpu(self, stdout):
        """
        Get useful data from NCIPlot stdout, or any file-like object.
        """
        data = {}
        data['_raw'] = []
        for line in stdout:
            if line.startswith('#') or line.startswith('---'):
                continue
            elif line.lstrip().startswith('RHO'):
                data['rho'] = float(line.split()[-1].strip())
            elif line.lstrip().startswith('RDG'):
                data['rdg'] = float(line.split()[-1].strip())
            elif line.rstrip().endswith('-grad.cube'):
                data['grad_cube'] = os.path.join(self._tmpdir, line.split('=')[-1].strip())
            elif line.rstrip().endswith('-dens.cube'):
                data['dens_cube'] = os.path.join(self._tmpdir, line.split('=')[-1].strip())
            elif 'LS x RDG' in line:
                data['xy_data'] = os.path.join(self._tmpdir, line.split('=')[-1].strip())
            data['_raw'].append(line)
        return data

    def _parse_stdout_cuda(self, stdout):
        """
        Get useful data from CUDA NCIPlot [1] stdout, or any file-like object.

        [1] A GPU accelerated implementation of NCI calculations using pro-molecular
            density. Rubez G, Etancelin JM, Vigouroux X, Krajecki M, Boisson JC, 
            Henon E., J. Comput. Chem. 2017, 38, 1071
        """
        data = {}
        basedir = None
        data['_raw'] = []
        for line in stdout:
            data['_raw'].append(line)
            line = line.strip()
            if not line.startswith('*'):
                continue
            line = line.strip('*')
            if 'MoleculeFile' in line:
                basedir = os.path.split(line.split(':')[1].split()[0])[0]
            elif basedir and 'OutPut filenam Prefix' in line:
                basename = line.split(':')[1].strip()
                data['grad_cube'] = os.path.join(basedir, basename + '-RDG.cube')
                data['dens_cube'] = os.path.join(basedir, basename + '-dens.cube')
                data['xy_data'] = os.path.join(basedir, basename + '.dat')
            elif '.cube rho range' in line:
                data['rho'] = float(line.split()[6])
            elif '.dat rdg range' in line:
                data['rdg'] = float(line.split()[6])

        return data

    @staticmethod
    def create_nci_input(paths, output_level=3, ligand=None,
                         dat_cutoffs=(0.2, 1.0), cube_cutoffs=(0.07, 0.3), intermolecular=None,
                         radius=None, cube=None, increments=None, name=None, **kwargs):
        """
        Creates a file-like object with NCIPlot input options

        Parameters
        ----------
        paths : list of str
            Paths to XYZ molecule files.

        # Output options
        name : str, optional, default=None
            If set, replaces the output name, which by default is the molecule name
            without extension.
        output_level : int, optional, default=3
            How many files should NCI plot create. Level 1 is minimum output, level 3
            creates up to 4 files.
        dat_cutoffs : 2-tuple of float, optional, default=(0.2, 1.0)
            Density and RDG cutoffs used in creating the dat file
        cube_cutoffs : 2-tuple of float, optional, default=(0.07, 0.3)
            Density (r1) and RDG (r2) cutoffs used when creating the cube files. 
            r1 will set the cutoff for both the density and the RDG to be registered
            in the cube files, whereas r2 will be used for isosurfaces depiction.

        # Search options; If set, only a region will be explored. CHOOSE ONLY ONE!
        ligand : 2-tuple of float, int, optional, default=None
            If set, which molecule (by index) is working as a ligand, and the search
            radius to inspect within that molecule.
        intermolecular: float, optional, default=None
            If set, the search radius for interactions in between input molecules
        radius : 4-tuple of float, optional, default=None
            If set, first three values indicate the origin coordinates of the search
            sphere, and the fourth value indicates the search radius of such sphere.
        cube : 6-tuple of float, optional, default=None
            If set, search within the cube that is drawn between point A (first three
            floats) and point B (last three floats).
        increments : 3-tuple of float, optional, default=None
            ???

        Returns
        -------
        paths : list of str
            Collection of output files created by NCIPlot

        Notes
        -----
        NCI input files follow this scheme. More info @ 
        https://github.com/aoterodelaroza/nciplot

            <number of molecule files>
            <path to molecule file, xyz or wfn>
            <path to molecule file>...
            # Optional
            LIGAND n r #index of molecule in path list, search radius
            INTERMOLECULAR r # search radius
            RADIUS x y z r # coordinates center, radius
            CUBE x0 y0 z0 x1 y1 z1 # draw a cube from a to b
            INCREMENTS r1 r2 r3 
            CUTOFFS r1 r2 # density and RDG cutoffs for dat file; defaults: 0.2, 1.0
            CUTPLOT r1 r2 # density and RDG cutoffs for cube file; defaults: 0.07, 0.3
            ISORDG r # isosurface level; 0.3 for xyz mols, 0.5 for wfn mols
            OUTPUT [1-3] # level of output: 1 is minimal, 3 max
            ONAME str # tag name
        """

        output = StringIO()
        output.write('{}\n'.format(len(paths)))
        for path in paths:
            output.write('{}\n'.format(path))

        if output_level in (1, 2, 3):
            output.write('OUTPUT {}\n'.format(output_level))
        if name:
            output.write('ONAME {}\n'.format(name))
        if dat_cutoffs:
            output.write('CUTOFFS {} {}\n'.format(*dat_cutoffs[:2]))
        if cube_cutoffs:
            output.write('CUTPLOT {} {}\n'.format(*cube_cutoffs[:2]))

        if ligand:
            output.write('LIGAND {} {}\n'.format(*ligand[:2]))
        elif intermolecular:
            output.write('INTERMOLECULAR {}\n'.format(intermolecular))
        elif radius:
            output.write('RADIUS {} {} {} {}\n'.format(*radius[:4]))
        elif cube:
            output.write('CUBE {} {} {} {} {} {}\n'.format(*cube[:6]))
        elif increments:
            output.write('INCREMENTS {} {} {}\n'.format(*increments[:3]))

        output.seek(0)
        return output

    def _check_paths(self, binary, dat_directory):
        """
        Check if paths are OK, we need to bring up defaults, or if they
        have been never set
        """
        if not os.path.isfile(binary):
            raise UserError('Specified NCIplot binary path {} does not exist'.format(binary))
        if not os.path.isdir(dat_directory):
            raise UserError('Specified NCIplot dat library path {} does not exist'.format(dat_directory))
예제 #15
0
class ProfileOpalService:

  def __init__(self, pdbpath, expath, name, fig):
    from WebServices.AppService_types import ns0
    from WebServices.opal_client import OpalService
    self.pdbpath = pdbpath
    self.expath = expath
    self.name = name
    self.figure = fig

    pdbFile = ns0.InputFileType_Def("inputFile")
    pdbFile._name = "pdbfile"
    f = open(pdbpath)
    pdbFile._contents = f.read()
    f.close()
    files = [ pdbFile ]
    argList = "pdbfile"
    if expath:
      exFile = ns0.InputFileType_Def("inputFile")
      exFile._name = "exfile"
      f = open(expath)
      exFile._contents = f.read()
      f.close()
      files.append(exFile)
      argList += " exfile"
    
    try:
      self.opal = OpalService("SAXSService")
    except:
      import traceback, sys
      print "Traceback from SAXS request:"
      traceback.print_exc(file=sys.stdout)
      print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
      from chimera import NonChimeraError
      raise NonChimeraError("SAXS web service appears "
            "to be down.  See Reply Log "
            "for more details.")

    self.opal.launchJob(argList, _inputFile=files)
    from chimera.tasks import Task
    self.task = Task("SAXS " + self.name, self.cancelCB, self.statusCB)
    
  def cancelCB(self):
    self.task = None

  def statusCB(self):
    self.task.updateStatus(self.opal.currentStatus())
    if not self.opal.isFinished():
      self.opal.queryStatus()
      return
    self.task = None
    self.filemap = self.opal.getOutputs()
    if self.opal.isFinished() > 0:
      # Successful completion
      self.finished()
    else:
      # Failed
      from chimera import replyobj
      replyobj.error("SAXS %s failed; see Reply Log for more information"
		      % self.name)
      self.showFileContent("stdout.txt")
      self.showFileContent("stderr.txt")

  def finished(self):
    if self.expath:
      data = self.getFileContent("pdbfile_exfile.dat")
      p = read_profile_data(data, 3)
    else:
      data = self.getFileContent("pdbfile.dat")
      p = read_profile_data(data, 2)
    self.figure = plot_profile(p, self.name, self.figure)

  def getURLContent(self, url):
    import urllib2
    f = urllib2.urlopen(url)
    data = f.read()
    f.close()
    return data

  def getFileContent(self, filename):
    return self.getURLContent(self.filemap[filename])

  def showURLContent(self, title, url):
    from chimera import replyobj
    data = self.getURLContent(url)
    replyobj.message("%s\n-----\n%s-----\n" % (title, data))

  def showFileContent(self, filename):
    try:
      url = self.filemap[filename]
    except KeyError:
      from chimera import replyobj
      replyobj.message("SAXS profile: there is no file named \"%s\"" % filename)
    else:
      self.showURLContent("SAXS profile %s" % filename, url)
예제 #16
0
 def updateStatus(self, msg):
     chimera.statusline.show_message(
         '{}: {}'.format(self.title, msg),
         clickCallback=self._status_click_callback)
     Task.updateStatus(self, msg)
예제 #17
0
class Controller(object):
    def __init__(self, gui, model, *args, **kwargs):
        self.gui = gui
        self.model = model
        self.set_mvc()
        self.task = None
        self.subprocess = None
        self.queue = None
        self.progress = None
        self.ensemble = None
        self.movie_dialog = None
        self.molecule = None
        self._last_steps = 0

    def set_mvc(self):
        self.gui.buttonWidgets['Save Input'].configure(command=self.saveinput)
        self.gui.buttonWidgets['Run'].configure(command=self.run)

    def run(self):
        if not self.saveinput():
            return
        env = os.environ.copy()
        env['OMMPROTOCOL_SLAVE'] = '1'
        env['PYTHONIOENCODING'] = 'latin-1'
        self.task = Task("OMMProtocol for {}".format(self.filename),
                         cancelCB=self._clear_cb,
                         statusFreq=((1, ), 1))
        self.subprocess = Popen(
            ['ommprotocol', self.filename],
            stdout=PIPE,
            stderr=PIPE,
            progressCB=self._progress_cb,  #universal_newlines=True,
            bufsize=1,
            env=env)
        self.progress = SubprocessTask("OMMProtocol",
                                       self.subprocess,
                                       task=self.task,
                                       afterCB=self._after_cb)
        self.task.updateStatus("Running OMMProtocol")
        self.queue = LifoQueue()
        thread = Thread(target=enqueue_output,
                        args=(self.subprocess.stdout, self.queue))
        thread.daemon = True  # thread dies with the program
        thread.start()
        self.ensemble = _TrajProxy()
        self.molecule = self.ensemble.molecule = self.gui.ui_chimera_models.getvalue(
        )
        self.ensemble.name = 'Trajectory for {}'.format(self.molecule.name)
        self.ensemble.startFrame = self.ensemble.endFrame = 1
        self.movie_dialog = MovieDialog(self.ensemble, externalEnsemble=True)
        self.gui.Close()

    def _clear_cb(self, *args):
        self.task.finished()
        self.task, self.subprocess, self.queue, self.progress, self.molecule = [
            None
        ] * 5
        if self.movie_dialog is not None:
            self.movie_dialog.Close()
            self.movie_dialog = None

    def _after_cb(self, aborted):
        if aborted:
            self._clear_cb()
            return
        if self.subprocess.returncode:
            last = self.subprocess.stderr.readlines()[-1]
            msg = "OMMProtocol calculation failed! Reason: {}".format(last)
            self._clear_cb()
            raise chimera.UserError(msg)
        self.task.finished()
        chimera.statusline.show_message('Yay! MD Done!')

    def _progress_cb(self, process):
        try:
            chunk = self.queue.get_nowait()
        except Empty:
            return self._last_steps / self.model.total_steps

        steps, positions = pickle.loads(chunk)
        if steps == self._last_steps:
            self._last_steps / self.model.total_steps

        self._last_steps = steps

        # Update positions in MD Movie Dialog
        coordinates = np.array(positions) * 10.
        coordsets_so_far = len(self.molecule.coordSets)
        cs = self.molecule.newCoordSet(coordsets_so_far)
        cs.load(coordinates)
        self.ensemble.endFrame = self.movie_dialog.endFrame = coordsets_so_far + 1
        self.movie_dialog.moreFramesUpdate('', [], self.movie_dialog.endFrame)
        self.movie_dialog.plusCallback()

        return self._last_steps / self.model.total_steps

    def saveinput(self, path=None):
        self.model.parse()
        if path is None:
            path = asksaveasfilename(parent=self.gui.canvas,
                                     defaultextension='.yaml',
                                     filetypes=[('YAML', '*.yaml')])
        if not path:
            return
        self.write(path)
        self.gui.status('Written to {}'.format(path),
                        color='blue',
                        blankAfter=4)
        return True

    def write(self, output):
        # Write input
        self.filename = output
        with open(self.filename, 'w') as f:
            f.write('# Yaml input for OpenMM MD\n\n')
            f.write('# input\n')
            yaml.dump(self.model.md_input, f, default_flow_style=False)
            f.write('\n')
            f.write('# output\n')
            yaml.dump(self.model.md_output, f, default_flow_style=False)
            if self.model.md_hardware:
                f.write('\n# hardware\n')
                yaml.dump(self.model.md_hardware, f, default_flow_style=False)
            f.write('\n# conditions\n')
            yaml.dump(self.model.md_conditions, f, default_flow_style=False)
            f.write('\n# OpenMM system options\n')
            yaml.dump(self.model.md_systemoptions, f, default_flow_style=False)
            f.write('\n\nstages:\n')
            for stage in self.model.stages:
                yaml.dump([stage], f, indent=8, default_flow_style=False)
                f.write('\n')
def saveImage(filename=None, width=None, height=None, format=None,
	      units="pixels", description=None, supersample=None,
	      master=None, printMode=None, raytrace=False,
	      raytracePreview=None, raytraceWait=None,
	      raytraceKeepInput=None,
	      hideDialogs=True, statusMessages=True, raiseWindow=True,
	      task=None):
	if chimera.nogui and chimera.opengl_platform() != 'OSMESA':
		raise chimera.UserError, "Need graphics to save images (or use headless Linux version)"
	if statusMessages:
		from replyobj import status
	else:
		def status(*args, **kw):
			pass
	if printMode is None:
		printMode = chimera.viewer.camera.mode()
	horizPixels, vertPixels, supersample = \
			imageArgs(units, width, height, supersample)
	savedSD = None	# saved screen distance
	if units != 'pixels':
		adjustFOV = preferences.get(IMAGE_SETUP, ADJUST_FOV)
		if adjustFOV == 1 or (adjustFOV == ONLY_STEREO_CAMERAS
						and printMode != 'mono'):
			# if image is twice as wide as screen,
			# screenDistance is half as much
			savedSD = chimera.viewer.camera.screenDistance
			adjust = convert[units] / convert['millimeters']
			image_width = width * adjust
			adjust = chimera.viewer.camera.windowWidth / image_width
			chimera.viewer.camera.screenDistance *= adjust
	if raytrace:
		if not checkPovrayLicense():
			return
		# TODO: make default an argument or preference
		if raytraceWait is None:
			raytraceWait = preferences.get(POVRAY_SETUP, WAIT_POVRAY)
		if raytraceKeepInput is None:
			raytraceKeepInput = preferences.get(POVRAY_SETUP, KEEP_INPUT)
		if raytracePreview is None:
			raytracePreview = preferences.get(POVRAY_SETUP, SHOW_PREVIEW)
		quality = DEFAULT_JPEG_QUALITY
		format = 'PNG'
		if not chimera.nogui and not filename:
			if not master:
				master = tkgui.app
			chooseDialog = _ChooseFileDialog(filterSet='povray',
					format=format)
			result = chooseDialog.run(master)
			if result is None:
				status("Image save cancelled.")
				return
			filename, format = result[0]
			quality = chooseDialog.quality.get()
		if not filename:
			replyobj.error("Need filename for POV-Ray output")
			return
		if filename.endswith('.png') or filename.endswith('.jpg'):
			povfilename = filename[:-4] + '.pov'
			inifilename = filename[:-4] + '.ini'
		else:
			povfilename = filename + '.pov'
			inifilename = filename + '.ini'
		if task is None:
			from chimera.tasks import Task
			task = Task("raytrace image", None)
		task.updateStatus("Generating POV-Ray data file")
		import exports
		exports.doExportCommand('POV-Ray', povfilename)
		task.updateStatus("Generating POV-Ray parameter file")
		if savedSD is not None:
			chimera.viewer.camera.screenDistance = savedSD
		import SubprocessMonitor as SM
		cmd = [
			preferences.get(POVRAY_SETUP, POVRAY_EXE),
			inifilename,
			"+I%s" % povfilename,
			"+O%s" % filename,
			"+V"		# need verbose to monitor progress
		]
		inifile = open(inifilename, 'w')
		print >> inifile, (
			"Width=%d\n"
			"Height=%d\n"
			# add font path, CHIMERA/share/fonts
			"Library_Path=\"%s\"\n"
			"Bounding=On\n"
			"Bounding_Threshold=1\n"
			"Split_Unions=On\n"
			"Remove_Bounds=On\n"
			"Quality=%d"
		) % (
			horizPixels,
			vertPixels,
			os.path.join(os.environ['CHIMERA'], 'share', 'fonts'),
			preferences.get(POVRAY_SETUP, POV_QUALITY)
		)
		if not preferences.get(POVRAY_SETUP, ANTIALIAS):
			print >> inifile, "Antialias=Off"
		else:
			print >> inifile, (
				"Antialias=On\n"
				"Antialias_Threshold=%f\n"
				"Antialias_Depth=%d\n"
				"Sampling_Method=%d"
			) % (
				preferences.get(POVRAY_SETUP, ANTIALIAS_THRESHOLD),
				preferences.get(POVRAY_SETUP, ANTIALIAS_DEPTH),
				preferences.get(POVRAY_SETUP, ANTIALIAS_METHOD)
			)
		if not preferences.get(POVRAY_SETUP, JITTER):
			print >> inifile, "Jitter=Off"
		else:
			print >> inifile, (
				"Jitter=On\n"
				"Jitter_Amount=%f\n"
			) % (
				preferences.get(POVRAY_SETUP, JITTER_AMOUNT),
			)
		oa = preferences.get(POVRAY_SETUP, OUTPUT_ALPHA)
		if oa == 1 or (oa == 2 and chimera.bgopacity):
			if chimera.viewer.depthCue:
				replyobj.warning("Depth-cueing disables transparent background")
			print >> inifile, "Output_Alpha=On"
		if format in ('PNG', 'Stereo PNG'):
			print >> inifile, ("; output PNG\n"
							"Output_File_Type=N8")
		elif format in ('JPEG', 'Stereo JPEG'):
			print >> inifile, ("; output JPEG\n"
					"Output_File_Type=J%d") % quality
		elif format in ('PPM'):
			print >> inifile, ("; output PPM\n"
							"Output_File_Type=P")
		if chimera.nogui or not raytracePreview:
			print >> inifile, "Display=Off"
		else:
			print >> inifile, "Display=On"
			if not raytraceWait:
				print >> inifile, "Pause_when_Done=On"
		inifile.close()
		if raytraceKeepInput:
			inputs = []
		else:
			inputs = [ povfilename, inifilename ]
		def afterCB(aborted, inputs=inputs, outputs=[filename]):
			import os
			for fn in inputs:
				try:
					os.remove(fn)
				except OSError:
					pass
			if aborted:
				for fn in outputs:
					try:
						os.remove(fn)
					except OSError:
						pass
		task.updateStatus("Starting POV-Ray")
		try:
			subproc = SM.Popen(cmd, stdin=None, stdout=None,
						stderr=SM.PIPE, daemon=True)
		except OSError, e:
			raise chimera.UserError, "Unable run POV-Ray executable: %s" % e
		progress = povrayProgress(subproc)
		progress.start()
		subproc.setProgress(progress)
		info = 'running "%s"' % '" "'.join(cmd)
		if not chimera.nogui and statusMessages:
			from chimera import dialogs, tkgui
			dialogs.display(tkgui._ReplyDialog.name)
			replyobj.info(info + '\n')
		task.updateStatus(info)
		subprog = SM.monitor('running POV-Ray', subproc,
					title="POV-Ray progress",
					task=task,
					afterCB=afterCB)
		if raytraceWait:
			subprog.wait()
		return
예제 #19
0
class BlastpdbService:

	outputFile = "blast.out"

	def __init__(self, queryName, querySeq, evalue, matrix, finishCB):
		from WebServices.AppService_types import ns0
		from WebServices.opal_client import OpalService
		self.queryName = queryName
		inputFile = ns0.InputFileType_Def("inputFile")
		inputFile._name = "query.fa"
		inputFile._contents = self.makeFasta(queryName, querySeq)
		service = "BlastpdbServicePort"
		argList = "-i %s -o %s -e %s -M %s" % (
				self.inputFile._name, self.outputFile,
				evalue, matrix)
		self.opal = OpalService(service)
		self.opal.launchJob(argList, _inputFile=[inputFile])
		self.finishCB = finishCB
		from chimera.tasks import Task
		self.task = Task("blastpdb %s" % queryName, self.cancelCB,
								self.statusCB)

	def cancelCB(self):
		self.task = None

	def statusCB(self):
		self.task.updateStatus(self.opal.currentStatus())
		if not self.opal.isFinished():
			self.opal.queryStatus()
			return
		self.task = None
		fileMap = self.opal.getOutputs()
		if self.opal.isFinished() > 0:
			# Successful completion
			self.finishCB(self.getURLContent(
						fileMap[self.outputFile]))
		else:
			# Failed
			from chimera import replyobj
			replyobj.error("blastpdb %s failed; "
					"see Reply Log for more information"
					% self.queryName)
			try:
				self.showURLContent("blastpdb stdout",
							fileMap["stdout"])
			except KeyError:
				pass
			try:
				self.showURLContent("blastpdb stderr",
							fileMap["stderr"])
			except KeyError:
				pass

	def makeFasta(self, name, seq):
		output = [ ">QUERY\n" ]
		maxLine = 60
		for i in range(0, len(seq), maxLine):
			end = min(i + maxLine, len(seq))
			output.append("%s\n" % seq[i:end])
		return ''.join(output)

	def getURLContent(self, url):
		import urllib2
		f = urllib2.urlopen(url)
		data = f.read()
		f.close()
		return data

	def showURLContent(self, title, url):
		from chimera import replyobj
		data = self.getURLContent(url)
		replyobj.message("%s\n-----\n%s-----\n" % (title, data))
예제 #20
0
 def start(self):
     self.newFrameHandle = chimera.triggers.addHandler(
         'post-frame', self.captureImage, None)
     self.RECORDING = True
     from chimera.tasks import Task
     self.task = Task("record movie", self.cancelCB)
예제 #21
0
class ProfileOpalService:
    def __init__(self, pdbpath, expath, name, fig):
        from WebServices.AppService_types import ns0
        from WebServices.opal_client import OpalService
        self.pdbpath = pdbpath
        self.expath = expath
        self.name = name
        self.figure = fig

        pdbFile = ns0.InputFileType_Def("inputFile")
        pdbFile._name = "pdbfile"
        f = open(pdbpath)
        pdbFile._contents = f.read()
        f.close()
        files = [pdbFile]
        argList = "pdbfile"
        if expath:
            exFile = ns0.InputFileType_Def("inputFile")
            exFile._name = "exfile"
            f = open(expath)
            exFile._contents = f.read()
            f.close()
            files.append(exFile)
            argList += " exfile"

        try:
            self.opal = OpalService("SAXSService")
        except:
            import traceback, sys
            print "Traceback from SAXS request:"
            traceback.print_exc(file=sys.stdout)
            print """
Typically, if you get a TypeError, it's a problem on the remote server
and it should be fixed shortly.  If you get a different error or
get TypeError consistently for more than a day, please report the
problem using the Report a Bug... entry in the Help menu.  Please
include the traceback printed above as part of the problem description."""
            from chimera import NonChimeraError
            raise NonChimeraError("SAXS web service appears "
                                  "to be down.  See Reply Log "
                                  "for more details.")

        self.opal.launchJob(argList, _inputFile=files)
        from chimera.tasks import Task
        self.task = Task("SAXS " + self.name, self.cancelCB, self.statusCB)

    def cancelCB(self):
        self.task = None

    def statusCB(self):
        self.task.updateStatus(self.opal.currentStatus())
        if not self.opal.isFinished():
            self.opal.queryStatus()
            return
        self.task = None
        self.filemap = self.opal.getOutputs()
        if self.opal.isFinished() > 0:
            # Successful completion
            self.finished()
        else:
            # Failed
            from chimera import replyobj
            replyobj.error(
                "SAXS %s failed; see Reply Log for more information" %
                self.name)
            self.showFileContent("stdout.txt")
            self.showFileContent("stderr.txt")

    def finished(self):
        if self.expath:
            data = self.getFileContent("pdbfile_exfile.dat")
            p = read_profile_data(data, 3)
        else:
            data = self.getFileContent("pdbfile.dat")
            p = read_profile_data(data, 2)
        self.figure = plot_profile(p, self.name, self.figure)

    def getURLContent(self, url):
        import urllib2
        f = urllib2.urlopen(url)
        data = f.read()
        f.close()
        return data

    def getFileContent(self, filename):
        return self.getURLContent(self.filemap[filename])

    def showURLContent(self, title, url):
        from chimera import replyobj
        data = self.getURLContent(url)
        replyobj.message("%s\n-----\n%s-----\n" % (title, data))

    def showFileContent(self, filename):
        try:
            url = self.filemap[filename]
        except KeyError:
            from chimera import replyobj
            replyobj.message("SAXS profile: there is no file named \"%s\"" %
                             filename)
        else:
            self.showURLContent("SAXS profile %s" % filename, url)