Source code for ccpn.ui.gui.modules.PyMolUtil

"""
Module Documentation here
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2021"
__credits__ = ("Ed Brooksbank, Joanna Fox, Victoria A Higman, Luca Mureddu, Eliza Płoskoń",
               "Timothy J Ragan, Brian O Smith, Gary S Thompson & Geerten W Vuister")
__licence__ = ("CCPN licence. See http://www.ccpn.ac.uk/v3-software/downloads/license")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$Date: 2021-04-26 12:13:48 +0100 (Mon, April 26, 2021) $"
__dateModified__ = "$dateModified: 2021-11-09 18:38:41 +0000 (Tue, November 09, 2021) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Luca Mureddu $"
__date__ = "$Date: 2021-04-26 12:13:48 +0100 (Mon, April 26, 2021) $"
#=========================================================================================
# Start of code
#=========================================================================================

import os
import random
import subprocess
from ccpn.ui.gui.widgets import MessageDialog
from ccpn.util.Logging import getLogger
from collections import deque
from ccpn.util.Colour import brightColours, hexToRgb


def _getDefaultScriptHeader():
    """
    Default Header warning for AutoGenerated Scripts
    """
    from ccpn.framework.Application import getApplication
    application = getApplication()
    programName, version = 'CcpNmrAnalysis', '3'
    if application:
        programName, version = application.applicationName, application.applicationVersion
    warning  = (' Warning: This script is auto-generated. Any changes here will be lost.')
    versLine = (" %s, version: %s" % (programName, version))
    spacer   = ('='* (len(warning) + 8))
    lines = []
    lines.append("")
    lines.append(spacer)
    lines.append("")
    lines.append(versLine)
    lines.append(warning)
    lines.append("")
    lines.append(spacer)
    return '\n'.join(lines)


[docs]class CodeBlock(): """ A class to concatenate multiple code lines for an easy export to file. Used for creating dynamically scripting files to be handled by external programs. Usage: CB = CodeBlock() CB.addImport('from x import y') CB.addCmd('myFunc','astring', myStr='BB', myBool=True, myInt=22, myFloat=1.3) CB.toFile('~/testScript.py') print(str(CB)) out: from x import y myFunc("astring", myStr="BB", myBool=True, myInt=22, myFloat=1.3) """ def __init__(self): self._block = deque() self._fileHeader = _getDefaultScriptHeader() def __str__(self): """ A string representation of the code block. Imports appear at the top, if added with addImport method. """ result = f'"""{self._fileHeader}\n""" \n\n' result += """\n""".join(self._block) return result @property def fileHeader(self): """The message that will appear on top of the file, before any code.""" return self._fileHeader @fileHeader.setter def fileHeader(self, text): self._fileHeader = text
[docs] def toFile(self, filePath): """ dump block to a file """ with open(filePath, 'w') as f: f.write(str(self))
[docs] def addCmd(self, funcName:str, *args, **kwargs): """ add a function call to the block. :param funcName: the function name to be called in the script :param args: any arg that the func needs, in the same original signature order and format. :param kwargs: any kwargs that the func needs, in any order but with the correct formatting. E.g.: addCmd('cmd.pseudoatom','theName', resn='PSD', pos=None, q=0.0, mode='rms', quiet=1) """ _args = ",".join(filter(None, (f"""'{v}'""" for v in args))) f = [] for k, v in kwargs.items(): if isinstance(v, str): a = f"""{k}='{v}'""" # wrap with quotes if the value format is a string else: # make sure the value format is maintained correctly a = f"""{k}={v}""" f.append(a) _kwargs = ", ".join(filter(None, f )) _all = ", ".join(filter(None, [_args, _kwargs])) cmd = f"""{funcName}({_all})""" self._block.append(cmd)
[docs] def addImport(self, theImport, *args, **kwargs): self._block.appendleft(f"""{theImport}""")
[docs] def addFreeLine(self, line): self._block.append(f"""{line}""")
#################################################################################### ############################ PyMOL Scripts ######################################## ####################################################################################
[docs]def runPymolWithScript(application, pymolScriptPath): """ run pymol executing a script """ mainWindow = application.ui.mainWindow pymolAppPath = application.preferences.externalPrograms.pymol if not os.path.exists(pymolAppPath): ok = MessageDialog.showOkCancelWarning('Molecular Viewer not Set', 'Select the executable file on preferences') if ok: from ccpn.ui.gui.popups.PreferencesPopup import PreferencesPopup pp = PreferencesPopup(parent=mainWindow, mainWindow=mainWindow, preferences=application.preferences) pp.tabWidget.setCurrentIndex(pp.tabWidget.count() - 1) pp.exec_() return try: pymolProcess = subprocess.Popen(pymolAppPath + ' -r ' + pymolScriptPath, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except Exception as e: getLogger().warning('Pymol not started. Check executable.', e)
def _CSMSelection2PyMolFile(filePath, pdbPath, aboveThresholdResidues, belowThresholdResidues, missingdResidues, colourMissing, colourAboveThreshold, colourBelowThreshold, selection): """ _CCPNnmr Internal. Used in ChemicalShift mapping Module This creates a file with a PyMol script to mirror the chemical shift mapping module selections etc...""" _mapDict = { 'above': { 'colour' : colourAboveThreshold, 'residues': aboveThresholdResidues, }, 'below': { 'colour' : colourBelowThreshold, 'residues': belowThresholdResidues, }, 'missing': { 'colour' : colourMissing, 'residues': missingdResidues, } } if os.path.exists(pdbPath): codeBlock = CodeBlock() imp = codeBlock.addImport cmd = codeBlock.addCmd imp('from pymol import cmd') cmd('cmd.load', pdbPath) cmd('cmd.hide', 'lines') cmd('cmd.show', 'cartoon') cmd('cmd.color', 'white') for cond in _mapDict: if len(_mapDict.get(cond).get('residues', [])) > 0: cmd('cmd.select', f'{cond}Threshold', f'res {_mapDict.get(cond).get("residues")}') cmd('cmd.set_color', f'{cond.upper()}Colour', f'{_mapDict.get(cond).get("colour")}') cmd('cmd.color', f'{cond.upper()}Colour', f'{cond}Threshold') if len(selection) > 0: cmd('cmd.select', 'Selected', f'res {selection}') else: cmd('cmd.deselect') codeBlock.toFile(filePath) return filePath def _restraintsSelection2PyMolFile(pymolScriptPath, pdbPath, restraints): """ _CCPNnmr Internal. Used in RestraintTable Module. It fills the file pymolScriptPath with a PyMol script. """ import ccpn.ui.gui.modules.RestraintTableModule as rt pseudoAtomColour = 'teal' atomTextColour = 'teal' distColour = 'red' residueShape = 'sticks' residueColour = 'teal' atomShape = 'nb_spheres' codeBlock = CodeBlock() imp = codeBlock.addImport cmd = codeBlock.addCmd imp('from pymol import cmd') cmd('cmd.load', pdbPath) cmd('cmd.util.cbaw',) cmd('cmd.dss', ) cmd('cmd.show', 'cartoon') cmd('cmd.hide', 'lines') for restraint in restraints: rID = str(restraint.serial) atomPair = rt.GuiRestraintTable.getFirstRestraintAtomsPair(restraint) selAtoms = {} for i, atom in enumerate(atomPair): _selName = f'Sel_RS_{rID}_{"Atom"}_{str(i+1)}' sticksSelection = f'{atom.residue.chain.name}/{atom.residue.sequenceCode}/' if atom.componentAtoms: # Do PseudoAtoms: atomGroup = [ag for ag in atom.componentAtoms if not ag.componentAtoms] subAtoms = [sa for ag in atom.componentAtoms for sa in ag.componentAtoms if ag.componentAtoms] #E.g. for VAL HG% -> will include all atoms HG11,12,13 and HG21,22,23 atomNames = '+'.join([singleAtom.name for singleAtom in list(set(atomGroup + subAtoms))]) selectionName = f'Temp_Sel_RS_{rID}_{str(i+1)}' selection = f'{atom.residue.chain.name}/{atom.residue.sequenceCode}/{atomNames}' pseudoAtomName = f'RS_{rID}_{"PSA"}_{str(i+1)}' # can't put symbols on Pymol objects. cmd('cmd.select', selectionName, selection=selection) cmd('cmd.pseudoatom', object=pseudoAtomName, selection=selection, name=atom.name, chain=atom.residue.chain.name, resn=atom.residue.residueType, resi=atom.residue.sequenceCode, color=pseudoAtomColour, label=atom.id,state=-1) cmd('cmd.delete', selectionName) cmd('cmd.select', name=_selName, selection=pseudoAtomName) cmd('cmd.show', atomShape, selection=_selName) cmd('cmd.show', residueShape, selection=sticksSelection) cmd('cmd.color', residueColour, sticksSelection) selAtoms[_selName] = atom else: # Do normal atom _selection = f'{atom.residue.chain.name}/{atom.residue.sequenceCode}/{atom.name}' cmd('cmd.select', name=_selName, selection=_selection) selAtoms[_selName] = atom cmd('cmd.show', atomShape, selection=_selName) cmd('cmd.show', residueShape, selection=sticksSelection) cmd('cmd.color', residueColour, sticksSelection) cmd('cmd.label', selection=f'{atom.residue.sequenceCode}/{atom.name}', expression='str("%s")' %atom.id) if len(list(selAtoms.keys())) == 2: distanceName = 'RS_%s' % rID selNames = list(selAtoms.keys()) cmd('cmd.distance', name=distanceName, selection1=selNames[0], selection2=selNames[1]) cmd('cmd.color', distColour, distanceName) for selName in selNames: cmd('cmd.delete', selName) codeBlock.toFile(pymolScriptPath) return pymolScriptPath # def _randomColour(): # https://pymolwiki.org/index.php/Color_Values # colour = random.choice(list(brightColours.keys())) # colourName = brightColours[colour] # cmd('cmd.set_color', colourName, hexToRgb(colour)) # cmd('cmd.color', colourName, distanceName)