Source code for ccpn.ui.gui.popups.NmrResiduePopup

#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (https://www.ccpn.ac.uk) 2014 - 2022"
__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 https://ccpn.ac.uk/software/licensing/")
__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: 2022-02-15 12:03:57 +0000 (Tue, February 15, 2022) $"
__version__ = "$Revision: 3.1.0 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: CCPN $"
__date__ = "$Date: 2017-04-07 10:28:41 +0000 (Fri, April 07, 2017) $"
#=========================================================================================
# Start of code
#=========================================================================================

import re
from ccpn.core.NmrResidue import NmrResidue, _getNmrResidue
from ccpn.core.lib.AssignmentLib import CCP_CODES_SORTED, getNmrResiduePrediction
from ccpn.ui.gui.popups.AttributeEditorPopupABC import AttributeEditorPopupABC, _attribContainer
from ccpn.ui.gui.widgets.CompoundWidgets import EntryCompoundWidget, PulldownListCompoundWidget, CheckBoxCompoundWidget
from ccpn.ui.gui.widgets.MessageDialog import showWarning, showYesNoWarning
from ccpn.util.OrderedSet import OrderedSet


REMOVEPERCENT = '( ?\d+.?\d* ?%)+'
MERGE = 'merge'
CREATE = 'create'
DEASSIGN = 'deassign'


def _getResidueTypeProb(self, currentNmrResidue):
    """Get the probabilities of the residueTypes
    """
    # ignore if no chemical shifts or a <new> nmrResidue - which chemicalShiftList?
    if self.project.chemicalShiftLists and len(self.project.chemicalShiftLists) > 0 and not isinstance(currentNmrResidue, _attribContainer):
        predictions = getNmrResiduePrediction(currentNmrResidue, self.project.chemicalShiftLists[0])
        preds1 = [' '.join([x[0].upper(), x[1]]) for x in predictions]  # if not currentNmrResidue.residueType]
        preds1 = list(OrderedSet(preds1))
        remainingResidues = list(CCP_CODES_SORTED)
        possibilities = [currentNmrResidue.residueType] + preds1 + remainingResidues
    else:
        possibilities = ('',) + CCP_CODES_SORTED
    self.residueType.modifyTexts(possibilities)


def _checkNmrResidue(self, value=None):
    """Check the new pulldown item and strip bad characters
    """
    # Check the correct characters for residueType - need to remove spaceNumberPercent
    value = re.sub(REMOVEPERCENT, '', value)
    if value not in self.residueType.pulldownList.texts:
        # add modified value if not in the pulldown
        self.residueType.pulldownList.addItem(value)
    self.residueType.pulldownList.set(value)


[docs]class NmrResidueEditPopup(AttributeEditorPopupABC): """ NmrResidue attributes editor popup New checkBox Create New if checked if the nmrChain doesn't exist, create a new nmrChain If the sequenceCode doesn't exist, create a new nmrResidue else if merge checked merge into the existing nmrResidue, if found else rename the current nmrResidue sequenceCode and residueType """ def _getNmrChainList(self, nmrChain): """Populate the nmrChain pulldown """ self.nmrChain.modifyTexts([x.id for x in self.project.nmrChains]) self.nmrChain.select(self.obj.nmrChain.id) klass = NmrResidue attributes = [('Pid', EntryCompoundWidget, getattr, None, None, None, {}), ('NmrChain', PulldownListCompoundWidget, getattr, setattr, _getNmrChainList, None, {}), ('Sequence Code', EntryCompoundWidget, getattr, setattr, None, None, {}), # ('Create New', CheckBoxCompoundWidget, None, None, None, None, {'checked': False, 'checkable':True}), ('Residue Type', PulldownListCompoundWidget, getattr, setattr, _getResidueTypeProb, _checkNmrResidue, {}), ('Merge to Existing', CheckBoxCompoundWidget, None, None, None, None, {'checked': False, 'checkable':True}), # ('Deassign', CheckBoxCompoundWidget, None, None, None, None, {'checked': False, 'checkable':True}), ('Comment', EntryCompoundWidget, getattr, setattr, None, None, {'backgroundText': '> Optional <'}), ] # hWidth = 120 # def __init__(self, *args, **kwds): # super().__init__(*args, **kwds) # self.deassign.checkBox.clicked.connect(self._deassignCallBack) # self.mergetoExisting.checkBox.clicked.connect(self._mergetoExistingCallBack) def __postInit__(self): """post initialise functions - required as may need to insert more objects into dialog """ # add a new button to deassign/disconnect nmrResidue self.setUserButton(callback=self._userCallback, tipText='Deassign the nmrResidue and return to the default nmrChain NC:@-', text='Deassign', enabled=True) super().__postInit__() self._userButton = self.dialogButtons.button(self.USERBUTTON) self._userButton.setEnabled(True) self._deassignClicked = False # def _deassignCallBack(self, *args): # """Handle clicking the deassign checkBox # - they are mutually exclusive # """ # if self.deassign.isChecked(): # self.mergetoExisting.set(False) # # def _mergetoExistingCallBack(self, *args): # """Handle clicking the mergetoExisting checkBox # - they are mutually exclusive # """ # if self.mergetoExisting.isChecked(): # self.deassign.set(False) def _userCallback(self, *args): """Handle deassign/disconnect selected nmrResidue """ with self.handleUserClicked() as error: # # deassign/disconnect the nmrResidue - nmrChains are split, and nmrResidue is returned to the pool # _seqCode = self.sequenceCode.getText() # _resType = re.sub(REMOVEPERCENT, '', self.residueType.getText()) # if _seqCode: # showWarning(str(self.windowTitle()), 'To deassign the nmrResidue, please leave the sequenceCode blank.') # error.errorValue = True # else: # self.obj.disconnect() # if _seqCode or _resType: # # reset the sequenceCode/residueType # self.obj.moveToNmrChain(sequenceCode=_seqCode, residueType=_resType) self.obj.disconnect() # self._deassignClicked = True # self._okClicked() # self._deassignClicked = False def _applyAllChanges(self, changes): """Apply all changes - move nmrResidue to new chain as required """ # if self._deassignClicked: # print(' beep') # if self.sequenceCode.getText(): # raise RuntimeError('To deassign the nmrResidue, please leave the sequenceCode blank.') # else: # self.obj.disconnect() # return merge = self.mergetoExisting.isChecked() create = False # self.createNew.isChecked() deassign = False # self.deassign.isChecked() if deassign: pass # if self.sequenceCode.getText(): # raise RuntimeError('To deassign the nmrResidue, please leave the sequenceCode blank.') # else: # self.obj.disconnect() else: _chainId = self.nmrChain.getText() _chainPid = 'NC:{}'.format(self.nmrChain.getText()) # create a new nmrChain as required _nmrChain = self.project.fetchNmrChain(_chainId) # find the existing nmrResidue destNmrResidue = _getNmrResidue(_nmrChain, self.sequenceCode.getText(), re.sub(REMOVEPERCENT, '', self.residueType.getText())) if _nmrChain else None if destNmrResidue and (self.obj != destNmrResidue): # move to an existing nmrResidue requires a merge _ok = True if not merge: # popup warning if merge not specified _msg = 'Cannot move NmrResidue to an existing NmrResidue without merging.\n\nDo you want to merge?' _ok = showYesNoWarning(str(self.windowTitle()), _msg) if _ok: destNmrResidue.mergeNmrResidues(self.obj) destNmrResidue.comment = self.comment.getText() else: if not create or (self.obj == destNmrResidue): # assign to a new nmrResidue, includes changing just the residueType self.obj.moveToNmrChain(_chainPid, self.sequenceCode.getText(), re.sub(REMOVEPERCENT, '', self.residueType.getText()) ) else: # fetch a new residue, sequenceCode has changed self.obj = _nmrChain.fetchNmrResidue(self.sequenceCode.getText(), re.sub(REMOVEPERCENT, '', self.residueType.getText()) ) self.obj.comment = self.comment.getText()
[docs] def storeWidgetState(self): """Store the state of the checkBoxes between popups """ merge = self.mergetoExisting.isChecked() # create = self.createNew.isChecked() # deassign = self.deassign.isChecked() NmrResidueEditPopup._storedState[MERGE] = merge
# NmrResidueEditPopup._storedState[CREATE] = create # NmrResidueEditPopup._storedState[DEASSIGN] = deassign
[docs] def restoreWidgetState(self): """Restore the state of the checkBoxes """ self.mergetoExisting.set(NmrResidueEditPopup._storedState.get(MERGE, False))
# self.createNew.set(NmrResidueEditPopup._storedState.get(CREATE, False)) # self.deassign.set(NmrResidueEditPopup._storedState.get(DEASSIGN, False)) def _setValue(self, attr, setFunction, value): """Not needed here - subclass so does no operation """ pass
[docs]class NmrResidueNewPopup(AttributeEditorPopupABC): """ NmrResidue attributes new popup """ def _getNmrChainList(self, nmrChain): """Populate the nmrChain pulldown """ self.nmrChain.modifyTexts([x.id for x in self.project.nmrChains]) self.nmrChain.select(self._parentNmrChain.id) klass = NmrResidue attributes = [('NmrChain', PulldownListCompoundWidget, getattr, setattr, _getNmrChainList, None, {}), ('Sequence Code', EntryCompoundWidget, getattr, setattr, None, None, {}), ('Residue Type', PulldownListCompoundWidget, getattr, setattr, _getResidueTypeProb, _checkNmrResidue, {}), ('Comment', EntryCompoundWidget, getattr, setattr, None, None, {'backgroundText': '> Optional <'}), ] def __init__(self, parent=None, mainWindow=None, obj=None, **kwds): self._parentNmrChain = obj super().__init__(parent=parent, mainWindow=mainWindow, obj=obj, **kwds) def _applyAllChanges(self, changes): """Apply all changes - move nmrResidue to new chain as required """ _chainId = self.nmrChain.getText() _chainPid = 'NC:{}'.format(self.nmrChain.getText()) # create a new nmrChain as required _nmrChain = self.project.fetchNmrChain(_chainId) # find the existing nmrResidue destNmrResidue = _getNmrResidue(_nmrChain, self.sequenceCode.getText(), re.sub(REMOVEPERCENT, '', self.residueType.getText())) if _nmrChain else None if not destNmrResidue: self.obj = _nmrChain.fetchNmrResidue(self.sequenceCode.getText(), re.sub(REMOVEPERCENT, '', self.residueType.getText()) ) self.obj.comment = self.comment.getText()
[docs] def storeWidgetState(self): """Store the state of the checkBoxes between popups """ pass
[docs] def restoreWidgetState(self): """Restore the state of the checkBoxes """ pass
def _setValue(self, attr, setFunction, value): """Not needed here - subclass so does no operation """ pass def _populateInitialValues(self): pass