Source code for ccpn.ui.gui.popups.PMIListPropertiesPopupABC
"""
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-11-09 15:26:01 +0000 (Tue, November 09, 2021) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Ed Brooksbank $"
__date__ = "$Date: 2020-04-16 12:14:51 +0000 (Thu, April 16, 2020) $"
#=========================================================================================
# Start of code
#=========================================================================================
from PyQt5 import QtWidgets
import ccpn.util.Colour as Colour
from functools import partial
from ccpn.ui.gui.widgets.MessageDialog import showWarning
from ccpn.ui.gui.widgets.Button import Button
from ccpn.ui.gui.widgets.Label import Label
from ccpn.ui.gui.widgets.LineEdit import LineEdit
from ccpn.ui.gui.widgets.PulldownList import PulldownList
from ccpn.ui.gui.widgets.DoubleSpinbox import DoubleSpinbox
from ccpn.ui.gui.widgets.CheckBox import CheckBox
from ccpn.ui.gui.widgets.Spacer import Spacer
from ccpn.ui.gui.widgets.ColourDialog import ColourDialog
from ccpn.ui.gui.popups.Dialog import CcpnDialogMainWidget, _verifyPopupApply
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGL import GLNotifier
from ccpn.core.lib.ContextManagers import undoStackBlocking, queueStateChange, notificationEchoBlocking
from ccpn.core._implementation.PMIListABC import PMIListABC #, MERITENABLED, MERITTHRESHOLD, \
# SYMBOLCOLOUR, TEXTCOLOUR, LINECOLOUR, MERITCOLOUR
from ccpn.util.AttrDict import AttrDict
from ccpn.util.Colour import spectrumColours, addNewColour, fillColourPulldown
from ccpn.ui.gui.lib.ChangeStateHandler import changeState
from ccpn.ui.gui.popups.AttributeEditorPopupABC import getAttributeTipText
from ccpn.util.Common import stringToCamelCase, camelCaseToString
from ccpn.ui.gui.popups.AttributeEditorPopupABC import _attribContainer
# define two groups of buttons for above/below the merit checkbox
BUTTONOPTIONS1 = (PMIListABC._SYMBOLCOLOUR, PMIListABC._TEXTCOLOUR, PMIListABC._LINECOLOUR, None)
BUTTONOPTIONS2 = (None, None, None, PMIListABC._MERITCOLOUR)
BUTTONOPTIONS = tuple(b1 or b2 for b1, b2 in zip(BUTTONOPTIONS1, BUTTONOPTIONS2))
[docs]class PMIListPropertiesPopupABC(CcpnDialogMainWidget):
"""Abstract Base Class for popups for Peak/Multiplet/Integral
"""
klass = None # The class whose properties are edited/displayed
attributes = [] # A list of (attributeName, getFunction, setFunction, kwds) tuples;
# options for displaying colours
_symbolColourOption = False
_textColourOption = False
_lineColourOption = False
_meritColourOption = False
_meritOptions = False
LIVEDIALOG = True # changes are reflected instantly
EDITMODE = True
def __init__(self, parent=None, mainWindow=None, ccpnList=None, spectrum=None, title=None, editMode=None, **kwds):
"""
Initialise the widget
"""
if editMode is not None:
self.EDITMODE = editMode
self.LIVEDIALOG = editMode
self.WINDOWPREFIX = 'Edit ' if editMode else 'New '
super().__init__(parent, setLayout=True, windowTitle=title, **kwds)
self.mainWindow = mainWindow
if self.mainWindow:
self.application = mainWindow.application
self.project = mainWindow.application.project
self.current = mainWindow.application.current
else:
self.application = None
self.project = None
self.current = None
self.spectrum = spectrum
if self.EDITMODE:
self.ccpnList = ccpnList
else:
self.ccpnList = self._newContainer()
self._populateInitialValues()
if not (self.ccpnList or spectrum):
showWarning(title, 'No %s Found' % self.klass.className)
self.close()
self._colourPulldowns = []
# add the first row with the name of the listView
row = 0
# Label(self.mainWidget, "Name: ", grid=(row, 0))
# self.ccpnListLabel = Label(self.mainWidget, '<None>', grid=(row, 1))
# row += 1
# add initial dialog options as specified by attributes list
self.labels = {} # An (attributeName, Label-widget) dict
self.edits = {} # An (attributeName, LineEdit-widget) dict
for _label, getFunction, setFunction, kwds in self.attributes:
attr = stringToCamelCase(_label)
tipText = getAttributeTipText(self.klass, attr)
editable = setFunction is not None
self.labels[attr] = Label(self.mainWidget, _label, grid=(row, 0))
self.edits[attr] = LineEdit(self.mainWidget, textAlignment='left', editable=editable,
vAlign='t', grid=(row, 1), **kwds)
self.edits[attr].textChanged.connect(partial(self._queueSetValue, attr, getFunction, setFunction, row))
if tipText:
self.labels[attr].setToolTip(tipText)
row += 1
# add first set of default colours as required
for colButton, enabled in zip(BUTTONOPTIONS1, (self._symbolColourOption, self._textColourOption, self._lineColourOption, self._meritColourOption)):
if colButton and enabled:
row += 1
self._addButtonOption(self._colourPulldowns, colButton, row)
# add the meritOption buttons
if self._meritOptions:
row += 1
self.meritEnabledLabel = Label(self.mainWidget, text="Use Merit Threshold ", grid=(row, 0))
tipText = getAttributeTipText(self.klass, PMIListABC._MERITENABLED)
if tipText:
self.meritEnabledLabel.setToolTip(tipText)
self.meritEnabledBox = CheckBox(self.mainWidget, grid=(row, 1), )
self.meritEnabledBox.toggled.connect(self._queueSetMeritEnabled)
row += 1
self.meritThresholdLabel = Label(self.mainWidget, text=camelCaseToString(PMIListABC._MERITTHRESHOLD), grid=(row, 0))
tipText = getAttributeTipText(self.klass, PMIListABC._MERITTHRESHOLD)
if tipText:
self.meritThresholdLabel.setToolTip(tipText)
self.meritThresholdData = DoubleSpinbox(self.mainWidget, grid=(row, 1), hAlign='l', decimals=2, step=0.01, min=0.0, max=1.0)
self.meritThresholdData.setMinimumCharacters(7)
self.meritThresholdData.valueChanged.connect(self._queueSetMeritThreshold)
# add second set of default colours as required
for colButton, enabled in zip(BUTTONOPTIONS2, (self._symbolColourOption, self._textColourOption, self._lineColourOption, self._meritColourOption)):
if colButton and enabled:
row += 1
self._addButtonOption(self._colourPulldowns, colButton, row)
# set the next available row for inserting new items from subclass
self._rowForNewItems = row + 1
self.GLSignals = GLNotifier(parent=self)
self.setOkButton(callback=self._okClicked, enabled=False)
self.setCancelButton(callback=self._cancelClicked)
self.setHelpButton(callback=self._helpClicked, enabled=False)
self.setRevertButton(callback=self._revertClicked, enabled=False)
self.setDefaultButton(CcpnDialogMainWidget.CANCELBUTTON)
self.ccpnListViews = self._getListViews(ccpnList)
self._getSettings()
def __postInit__(self):
"""post initialise functions - required as may need to insert more objects into dialog
"""
# add spacer to stop columns changing width
self._rowForNewItems += 1
Spacer(self.mainWidget, 2, 2,
QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding,
grid=(self._rowForNewItems, 3), gridSpan=(1, 1))
super().__postInit__()
self._okButton = self.dialogButtons.button(self.OKBUTTON)
self._cancelButton = self.dialogButtons.button(self.CANCELBUTTON)
self._helpButton = self.dialogButtons.button(self.HELPBUTTON)
self._revertButton = self.dialogButtons.button(self.RESETBUTTON)
self.COMPARELIST = AttrDict(self.listViewSettings)
self._populate()
def _populate(self):
"""Populate the widgets from listViewSettings
"""
# with self.blockWidgetSignals():
with notificationEchoBlocking():
with self._changes.blockChanges():
# populate widgets from settings
self._setWidgetSettings()
def _getChangeState(self):
"""Get the change state from the _changes dict
"""
if not self._changes.enabled:
return None
applyState = True
revertState = False
allChanges = True if self._changes else False
return changeState(self, allChanges, applyState, revertState, self._okButton, None, self._revertButton, self._currentNumApplies)
def _addButtonOption(self, pulldowns, attrib, row):
"""Add a labelled pulldown list for the selected attribute
"""
_label = camelCaseToString(attrib)
_colourLabel = Label(self.mainWidget, _label, grid=(row, 0))
_tipText = getAttributeTipText(self.klass, attrib)
if _tipText:
_colourLabel.setToolTip(_tipText)
_colourPulldownList = PulldownList(self.mainWidget, grid=(row, 1))
Colour.fillColourPulldown(_colourPulldownList, allowAuto=True, includeGradients=False)
pulldowns.append((_colourLabel, _colourPulldownList, attrib))
_colourButton = Button(self.mainWidget, hAlign='l', grid=(row, 2), hPolicy='fixed', #vAlign='t',
callback=partial(self._queueSetColourButton, _colourPulldownList), icon='icons/colours')
_colourPulldownList.currentIndexChanged.connect(partial(self._queueSetColour, _colourPulldownList, attrib, row))
def _getSettings(self):
"""Fill the settings dict from the listView object
"""
self.listViewSettings = {}
for item in self._colourPulldowns:
_, _, attrib = item
c = getattr(self.ccpnList, attrib, None)
self.listViewSettings[attrib] = c
self.listViewSettings[PMIListABC._MERITENABLED] = getattr(self.ccpnList, PMIListABC._MERITENABLED, False)
self.listViewSettings[PMIListABC._MERITTHRESHOLD] = getattr(self.ccpnList, PMIListABC._MERITTHRESHOLD, 0.0)
def _setWidgetSettings(self):
"""Populate the widgets from the settings dict
"""
# self.ccpnListLabel.setText(self.ccpnList.id)
for _label, getFunction, _, _ in self.attributes:
attr = stringToCamelCase(_label)
if getFunction and attr in self.edits:
value = getFunction(self.ccpnList, attr)
self.edits[attr].setText(str(value) if value is not None else '')
# add any new colours that may not be in the colour list
for item in self._colourPulldowns:
_, pl, attrib = item
c = self.listViewSettings[attrib]
if not Colour.isSpectrumColour(c):
Colour.addNewColourString(c)
# set the colours in the pulldowns
for item in self._colourPulldowns:
_, pl, attrib = item
Colour.fillColourPulldown(pl, allowAuto=True, includeGradients=False)
c = self.listViewSettings[attrib]
Colour.selectPullDownColour(pl, c, allowAuto=True)
self.meritEnabledBox.setChecked(self.listViewSettings[PMIListABC._MERITENABLED] or False)
self.meritThresholdData.setValue(self.listViewSettings[PMIListABC._MERITTHRESHOLD] or 0.0)
def _setListViewFromWidgets(self):
"""Set listView object from the widgets
"""
with notificationEchoBlocking():
with undoStackBlocking():
# # apply all functions to the object
# changes = self._changes
# if changes: # or not self.EDITMODE:
# self._applyAllChanges(changes)
for item in self._colourPulldowns:
_, pl, attrib = item
value = pl.currentText()
colour = Colour.getSpectrumColour(value, defaultReturn='#')
if colour is not None:
setattr(self.ccpnList, attrib, colour)
meritEnabled = self.meritEnabledBox.isChecked()
setattr(self.ccpnList, PMIListABC._MERITENABLED, meritEnabled)
meritThreshold = self.meritThresholdData.get()
setattr(self.ccpnList, PMIListABC._MERITTHRESHOLD, meritThreshold)
def _setListViewFromSettings(self):
"""Set listView object from the original settings dict
"""
with notificationEchoBlocking():
with undoStackBlocking():
for item in self._colourPulldowns:
_, pl, attrib = item
colour = self.listViewSettings[attrib]
if colour is not None:
setattr(self.ccpnList, attrib, colour)
if self.listViewSettings[PMIListABC._MERITENABLED] is not None:
setattr(self.ccpnList, PMIListABC._MERITENABLED, self.listViewSettings[PMIListABC._MERITENABLED])
if self.listViewSettings[PMIListABC._MERITTHRESHOLD] is not None:
setattr(self.ccpnList, PMIListABC._MERITTHRESHOLD, self.listViewSettings[PMIListABC._MERITTHRESHOLD])
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _changeSettings(self):
"""Widgets have been changed in the dialog
"""
self._setListViewFromWidgets()
# redraw the items
self._refreshGLItems()
def _getListViews(self, ccpnList):
"""Return the listViews containing this list
"""
# MUST BE SUBCLASSED
raise NotImplementedError("Code error: function not implemented")
def _okClicked(self):
"""Accept the dialog and disconnect all signals
"""
if self._changes or not self.EDITMODE:
# with undoStackBlocking():
# reset so the apply works correctly for undo/redo
self._setListViewFromSettings()
self._applyChanges()
self.accept()
def _revertClicked(self):
"""Handle the revert clicked button
"""
if self._changes:
# with notificationEchoBlocking():
# with undoStackBlocking():
self._setListViewFromSettings()
self._populate()
# redraw the items
self._refreshGLItems()
self._changes.clear()
# disable the buttons
self._okButton.setEnabled(False or not self.EDITMODE)
self._revertButton.setEnabled(False)
def _helpClicked(self):
pass
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _fillPullDowns(self):
for item in self._colourPulldowns:
_, pl, attrib = item
fillColourPulldown(pl, allowAuto=False, includeGradients=False)
@queueStateChange(_verifyPopupApply)
def _queueSetValue(self, attr, getFunction, setFunction, dim, _value):
"""Queue the function for setting the attribute in the calling object
"""
value = self.edits[attr].text()
oldValue = str(getFunction(self.ccpnList, attr))
if value != oldValue:
return partial(self._setValue, attr, setFunction, value)
def _setValue(self, attr, setFunction, value):
"""Function for setting the attribute, called by _applyAllChanges
"""
setFunction(self.ccpnList, attr, value)
def _queueSetColourButton(self, pulldown):
dialog = ColourDialog(self)
newColour = dialog.getColor()
if newColour:
addNewColour(newColour)
self._fillPullDowns()
pulldown.setCurrentText(spectrumColours[newColour.name()])
@queueStateChange(_verifyPopupApply)
def _queueSetColour(self, pl, attrib, dim, _value):
"""Queue the function for setting a colour attribute
"""
value = pl.currentText()
colour = Colour.getSpectrumColour(value, defaultReturn='#')
if colour != getattr(self.COMPARELIST, attrib, None):
return partial(self._setColour, attrib, colour)
def _setColour(self, attrib, value):
"""Function for setting the colour attribute, called by _applyAllChanges
"""
setattr(self.ccpnList, attrib, value)
@queueStateChange(_verifyPopupApply)
def _queueSetMeritEnabled(self, _value):
"""Queue the function for setting merit
"""
value = self.meritEnabledBox.get()
if value != getattr(self.COMPARELIST, PMIListABC._MERITENABLED, False):
return partial(self._setMeritEnabled, value)
def _setMeritEnabled(self, value):
"""Function for setting merit, called by _applyAllChanges
"""
setattr(self.ccpnList, PMIListABC._MERITENABLED, value)
@queueStateChange(_verifyPopupApply)
def _queueSetMeritThreshold(self, _value):
"""Queue the function for setting merit threshold
"""
value = self.meritThresholdData.get()
# set the state of the other buttons
textFromValue = self.meritThresholdData.textFromValue
if textFromValue(value) != textFromValue(getattr(self.COMPARELIST, PMIListABC._MERITTHRESHOLD, 0.0) or 0.0):
return partial(self._setMeritThreshold, value)
def _setMeritThreshold(self, value):
"""Function for setting merit threshold, called by _applyAllChanges
"""
setattr(self.ccpnList, PMIListABC._MERITTHRESHOLD, value)
def _newContainer(self):
"""Make a new container to hold attributes for objects not created yet
"""
return _attribContainer(self)
def _populateInitialValues(self):
"""Populate the initial values for an empty object
"""
# add second set of default colours as required
for colButton, enabled in zip(BUTTONOPTIONS, (self._symbolColourOption, self._textColourOption, self._lineColourOption, self._meritColourOption)):
if colButton and enabled:
setattr(self.ccpnList, colButton, '#')