Source code for ccpn.ui.gui.popups.CreateNmrChainPopup
"""
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__ = "$dateModified: 2021-08-04 12:28:19 +0100 (Wed, August 04, 2021) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Luca Mureddu $"
__date__ = "$Date: 2018-02-07 15:28:41 +0000 (Wed, February 02, 2018) $"
#=========================================================================================
# Start of code
#=========================================================================================
from ccpn.ui.gui.widgets.ButtonList import ButtonList
from ccpn.ui.gui.widgets.Label import Label
from ccpn.ui.gui.popups.Dialog import CcpnDialog
from ccpn.ui.gui.widgets.RadioButtons import RadioButtons
from ccpn.ui.gui.widgets.RadioButton import RadioButton
from ccpn.ui.gui.widgets.LineEdit import LineEdit
from ccpn.ui.gui.widgets.MessageDialog import showWarning
from ccpn.ui.gui.widgets.PulldownListsForObjects import NmrChainPulldown, ChainPulldown, SELECT, SubstancePulldown, ComplexPulldown
from ccpn.core.NmrChain import NmrChain
from ccpn.core.Chain import Chain
from ccpn.core.Substance import Substance
from ccpn.core.Complex import Complex
from ccpn.core.lib.ContextManagers import undoBlock, undoBlockWithoutSideBar, notificationEchoBlocking
CHAIN = Chain.className
NMRCHAIN = NmrChain.className
SUBSTANCE = Substance.className
COMPLEX = Complex.className
Cancel = 'Cancel'
Create = 'Create'
COPYNMRCHAIN = '-Copy'
CloneOptions = [CHAIN, NMRCHAIN, SUBSTANCE, COMPLEX]
CHAINTipText = 'Clones an NmrChain from a Chain. All the nmrResidues and nmrAtoms will be created.'
NMRCHAINTipText = 'Clones an NmrChain from a NmrChain. All the nmrResidues and nmrAtoms will be created.'
SUBSTANCETipText = 'Creates an NmrChain from a Substance which contains a single nmrResidue. If the SMILES is set, nmrAtoms will be created.'
COMPLEXTipText = 'Clones an nmrChain for each chain present in the complex. All the nmrResidues and nmrAtoms will be created.'
CloneOptionsTipTexts = [CHAINTipText, NMRCHAINTipText, SUBSTANCETipText, COMPLEXTipText]
[docs]class CreateNmrChainPopup(CcpnDialog):
def __init__(self, parent=None, mainWindow=None, project=None, **kwds):
# project is a 'dummy' argument to be compatible with the sideBar callback's
CcpnDialog.__init__(self, parent, setLayout=True, margins=(10,10,10,10),
windowTitle='Create NmrChain', size=(300, 200), **kwds)
self._parent = parent
self.mainWindow = mainWindow
self.project = project if project is not None else self.mainWindow.project
# GUI
vGrid = 0
self.createNewLabel = Label(self, text="Create New", grid=(vGrid, 0))
self.createNewWidget = RadioButton(self,
callback=self._selectCreateEmpty,
grid=(vGrid, 1),
)
vGrid += 1
self.addSpacer(0, 10, grid=(vGrid, 0))
vGrid += 1
self.cloneFromLabel = Label(self, text="Clone from", grid=(vGrid, 0))
self.cloneOptionsWidget = RadioButtons(self, texts=CloneOptions,
callback=self._cloneOptionCallback,
direction='v',
tipTexts=CloneOptionsTipTexts,
grid=(vGrid, 1),
vAlign='c'
)
vGrid += 1
self.availableChainsPD = ChainPulldown(self, self.mainWindow, showSelectName=True, callback=self._populateWidgets, labelText='', tipText=CHAINTipText,
grid=(vGrid, 1), )
self.availableChainsPD.label.hide()
self.availableChainsPD.hide()
vGrid += 1
self.availableNmrChainsPD = NmrChainPulldown(self, self.mainWindow, showSelectName=True, callback=self._populateWidgets, labelText='',
tipText=NMRCHAINTipText, grid=(vGrid, 1))
self.availableNmrChainsPD.label.hide()
self.availableNmrChainsPD.hide()
vGrid += 1
self.availableComplexesPD = ComplexPulldown(self, self.mainWindow, showSelectName=True,
callback=self._populateWidgets, labelText='', tipText=COMPLEXTipText, grid=(vGrid, 1))
self.availableComplexesPD.label.hide()
self.availableComplexesPD.hide()
vGrid += 1
tipText = SUBSTANCETipText
self.availableSubstancesPD = SubstancePulldown(self, self.mainWindow, showSelectName=True,
callback=self._populateWidgets, labelText='', tipText=tipText, grid=(vGrid, 1))
self.availableSubstancesPD.label.hide()
self.availableSubstancesPD.hide()
self.pulldownsOptions = {NMRCHAIN: self.availableNmrChainsPD, CHAIN: self.availableChainsPD,
SUBSTANCE: self.availableSubstancesPD, COMPLEX: self.availableComplexesPD}
vGrid += 1
self.addSpacer(0, 10, grid=(vGrid, 0))
vGrid += 1
self.labelName = Label(self, text="Name", grid=(vGrid, 0), )
self.nameLineEdit = LineEdit(self, grid=(vGrid, 1), textAlignment='left')
vGrid += 1
# self.spacerLabel = Label(self, text="", grid=(vGrid, 0))
self.addSpacer(0, 10, grid=(vGrid, 0))
vGrid += 1
self.buttonBox = ButtonList(self, texts=[Cancel, Create], callbacks=[self.reject, self._applyChanges],
grid=(vGrid, 0), gridSpan=(1,2), vAlign='bottom')
vGrid += 1
self._resetObjectSelections()
self._setCreateButtonEnabled(True)
self.createNewWidget.setChecked(True)
# self._activateCloneOptions()
self._selectCreateEmpty()
def _resetObjectSelections(self):
# used to create a new nmrChain from a selected object.
self._createEmpty = False
self._chain = None
self._nmrChain = None
self._substance = None
self._complex = None
def _selectCreateEmpty(self):
# Gui bit
self.cloneOptionsWidget.deselectAll()
if not self.createNewWidget.isChecked():
self.buttonBox.setButtonEnabled(Create, False)
else:
self._setCreateButtonEnabled(False)
for h in self.pulldownsOptions:
self.pulldownsOptions[h].hide()
# if self.nameLineEdit.receivers(self.nameLineEdit.textEdited): # may be other slots?
try:
self.nameLineEdit.textEdited.disconnect(self._cloneChainEdit)
except:
pass
finally:
self.nameLineEdit.textEdited.connect(self._newChainEdit)
# FIXME Not an elegant solution
self._resetObjectSelections()
self._createEmpty = True
def _newChainEdit(self):
self._setCreateButtonEnabled(True if self.nameLineEdit.text() else False)
def _cloneChainEdit(self):
for cl, pulldown in self.pulldownsOptions.items():
rButton = self.cloneOptionsWidget.getRadioButton(cl)
if rButton.isChecked():
self._setCreateButtonEnabled(True if self.nameLineEdit.text() and pulldown.getText() != SELECT else False)
break
def _createEmptyNmrChain(self, name):
if not self.project.getByPid(NmrChain.shortClassName + ':' + name):
return self.project.newNmrChain(name)
else:
showWarning('Existing NmrChain name.', 'Change name')
return
def _isNmrChainNameExisting(self, name):
if self.project.getByPid(NmrChain.shortClassName + ':' + name):
return True
else:
return False
def _setCreateButtonEnabled(self, value: bool = True):
self.buttonBox.setButtonEnabled(Create, value)
def _cloneFromChain(self, name):
from ccpn.util.isotopes import DEFAULT_ISOTOPE_DICT
with undoBlockWithoutSideBar():
with notificationEchoBlocking():
newNmrChain = self._createEmptyNmrChain(name)
if newNmrChain:
if len(self._chain.residues) > 0:
for residue in self._chain.residues:
nmrResidue = newNmrChain.newNmrResidue(sequenceCode=residue.sequenceCode,
residueType=residue.residueType)
for atom in residue.atoms:
isotopeCode = DEFAULT_ISOTOPE_DICT.get(atom.elementSymbol)
nmrResidue.newNmrAtom(atom.name, isotopeCode=isotopeCode) # is not a fetch but new. cannot be already one!
return newNmrChain
def _cloneFromNmrChain(self, name):
with undoBlockWithoutSideBar():
with notificationEchoBlocking():
newNmrChain = self._createEmptyNmrChain(name)
if newNmrChain:
if len(self._nmrChain.nmrResidues) > 0:
for nmrResidue in self._nmrChain.nmrResidues:
# need to check whether the mainResidue exists before creating the +/- residues
if nmrResidue.relativeOffset:
mainSequence = nmrResidue.mainNmrResidue.sequenceCode
newNmrResidue = newNmrChain.newNmrResidue(sequenceCode=mainSequence,
residueType=nmrResidue.residueType)
newNmrResidue = newNmrChain.newNmrResidue(sequenceCode=nmrResidue.sequenceCode, residueType=nmrResidue.residueType)
for nmrAtom in nmrResidue.nmrAtoms:
newNmrResidue.fetchNmrAtom(nmrAtom.name, isotopeCode=nmrAtom.isotopeCode)
return newNmrChain
def _cloneFromSubstance(self, name):
"""Create a new nmr chain from a substance which has a SMILES set."""
with undoBlockWithoutSideBar():
newNmrChain = self._createEmptyNmrChain(name)
if newNmrChain:
from ccpn.ui.gui.widgets.CompoundView import CompoundView, Variant, importSmiles
nmrResidue = newNmrChain.newNmrResidue(name)
if self._substance.smiles:
compound = importSmiles(self._substance.smiles, compoundName=name)
for atom in compound.atoms:
nmrAtom = nmrResidue.fetchNmrAtom(atom.name)
return newNmrChain
# def _disableSubstanceWithoutSMILES(self):
# 'disables from selection substances without SMILES'
# if self.project:
# for i, text in enumerate(self.availableSubstancesPD.textList):
# substance = self.project.getByPid(text)
# if isinstance(substance, Substance):
# if not substance.smiles:
# self.availableSubstancesPD.pulldownList.model().item(i).setEnabled(False)
def _applyChanges(self):
"""
The apply button has been clicked
If there is an error setting the values then popup an error message
repopulate the settings
"""
try:
self._createNmrChain()
except Exception as es:
showWarning(str(self.windowTitle()), str(es))
if self.mainWindow.application._isInDebugMode:
raise es
def _createNmrChain(self):
name = self.nameLineEdit.get()
if self.project:
# self.project.blankNotification()
if self._createEmpty:
newNmrChain = self._createEmptyNmrChain(name)
if newNmrChain:
self.accept()
else:
return
if self._chain:
newNmrChain = self._cloneFromChain(name)
if newNmrChain:
self.accept()
else:
return
if self._nmrChain:
newNmrChain = self._cloneFromNmrChain(name)
if newNmrChain:
self.accept()
else:
return
if self._substance:
newNmrChain = self._cloneFromSubstance(name)
if newNmrChain:
self.accept()
else:
return
if self._complex:
if name:
names = name.split(',')
for name in names:
exsistingNmrChain = self.project.getByPid(NmrChain.shortClassName + ':' + name)
if exsistingNmrChain:
showWarning('Existing NmrChain %s' % exsistingNmrChain.shortName, 'Change name')
return
if len(self._complex.chains) == len(names):
for chain, name in zip(self._complex.chains, names):
self._chain = chain
self._cloneFromChain(name)
self.accept()
else:
showWarning('Not enough names', 'Complex %s has %s chains, Please add the missing name/s'
% (self._complex.name, len(self._complex.chains)))
return
else:
return
self.accept()
def _populateWidgets(self, selected):
self._resetObjectSelections()
self.nameLineEdit.clear()
self._setCreateButtonEnabled(False)
obj = self.project.getByPid(selected)
if isinstance(obj, NmrChain):
self.nameLineEdit.setText(obj.shortName + COPYNMRCHAIN)
self._nmrChain = obj
self._setCreateButtonEnabled(True)
if isinstance(obj, Chain):
self._chain = obj
if self._isNmrChainNameExisting(self._chain.shortName):
self.nameLineEdit.setText(self._chain.shortName + COPYNMRCHAIN)
else:
self.nameLineEdit.setText(self._chain.shortName)
self._setCreateButtonEnabled(True)
if isinstance(obj, Substance):
self._substance = obj
if self._isNmrChainNameExisting(self._substance.name):
self.nameLineEdit.setText(self._substance.name + COPYNMRCHAIN)
else:
self.nameLineEdit.setText(self._substance.name)
self._setCreateButtonEnabled(True)
if isinstance(obj, Complex):
chainsNames = [chain.shortName for chain in obj.chains]
names = []
for name in chainsNames:
if self._isNmrChainNameExisting(name):
names.append(name + COPYNMRCHAIN)
else:
names.append(name)
if len(names) > 0:
self.nameLineEdit.setText(",".join(names))
self._complex = obj
self._setCreateButtonEnabled(True)
def _activateCloneOptions(self):
if self.project:
if len(self.project.substances) == 0:
rb = self.cloneOptionsWidget.getRadioButton(SUBSTANCE)
if rb:
rb.setEnabled(False)
# elif len(self.project.substances) > 0:
# noSmiles = [substance.smiles for substance in self.project.substances]
# if all(noSmiles):
# rb = self.cloneOptionsWidget.getRadioButton(SUBSTANCE)
# if rb:
# rb.setEnabled(False)
if len(self.project.chains) == 0:
rb = self.cloneOptionsWidget.getRadioButton(CHAIN)
if rb:
rb.setEnabled(False)
if len(self.project.complexes) == 0:
rb = self.cloneOptionsWidget.getRadioButton(COMPLEX)
if rb:
rb.setEnabled(False)
def _cloneOptionCallback(self):
self.createNewWidget.setChecked(False)
self._setCreateButtonEnabled(False)
selected = self.cloneOptionsWidget.getSelectedText()
# # needs to clear the previous selection otherwise has an odd behaviour from pulldownNofiers which remember the previous selection
self._resetObjectSelections()
for pd in self.pulldownsOptions:
self.pulldownsOptions[pd].select(SELECT)
if selected in self.pulldownsOptions:
self.pulldownsOptions[selected].show()
hs = [x for x in self.pulldownsOptions if x != selected]
for h in hs:
self.pulldownsOptions[h].hide()
# if self.nameLineEdit.receivers(self.nameLineEdit.textEdited):
try:
self.nameLineEdit.textEdited.disconnect()
except:
pass
finally:
self.nameLineEdit.textEdited.connect(self._cloneChainEdit)
if __name__ == '__main__':
from ccpn.ui.gui.widgets.Application import TestApplication
from ccpn.ui.gui.popups.Dialog import CcpnDialog
from ccpn.ui.gui.widgets.Widget import Widget
app = TestApplication()
popup = CreateNmrChainPopup()
widget = Widget(parent=popup, grid=(0, 0))
popup.show()
popup.raise_()
app.start()