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

"""
Module Documentation here
"""
#=========================================================================================
# 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-03-22 11:25:37 +0000 (Tue, March 22, 2022) $"
__version__ = "$Revision: 3.1.0 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Ed Brooksbank $"
__date__ = "$Date: 2021-10-29 16:38:09 +0100 (Fri, October 29, 2021) $"
#=========================================================================================
# Start of code
#=========================================================================================

from PyQt5 import QtWidgets
import pandas as pd
from ccpn.core.ViolationTable import ViolationTable as KlassTable

from ccpn.ui.gui.modules.CcpnModule import CcpnModule
from ccpn.ui.gui.widgets.Spacer import Spacer
from ccpn.ui.gui.widgets.HLine import HLine
from ccpn.ui.gui.widgets.Label import Label
from ccpn.ui.gui.widgets.LineEdit import LineEdit
from ccpn.ui.gui.widgets.PulldownListsForObjects import ViolationTablePulldown as KlassPulldown, RestraintTablePulldown
from ccpn.ui.gui.widgets.MessageDialog import showWarning
from ccpn.ui.gui.widgets.Frame import Frame
from ccpn.ui.gui.widgets.Splitter import Splitter
from ccpn.ui.gui.guiSettings import getColours, DIVIDER
from ccpn.ui.gui.lib._SimplePandasTable import _SimplePandasTableView, _updateSimplePandasTable
from ccpn.core.lib.ContextManagers import undoBlockWithoutSideBar

from ccpn.util.Logging import getLogger


ALL = '<all>'
_RESTRAINTTABLE = 'restraintTable'


#=========================================================================================
# ViolationTableModule
#=========================================================================================

[docs]class ViolationTableModule(CcpnModule): """ This class implements the module by wrapping a ViolationTable instance """ includeSettingsWidget = False maxSettingsState = 2 # states are defined as: 0: invisible, 1: both visible, 2: only settings visible settingsPosition = 'left' includePeakLists = False includeNmrChains = False includeSpectrumTable = False className = f'{KlassTable.className}Module' _allowRename = True activePulldownClass = None _includeInLastSeen = False def __init__(self, mainWindow=None, name=f'{KlassTable.className} Module', table=None, selectFirstItem=False): """ Initialise the Module widgets """ super().__init__(mainWindow=mainWindow, name=name) # Derive application, project, and current from mainWindow self.mainWindow = mainWindow if mainWindow: self.application = mainWindow.application self.project = mainWindow.application.project self.current = mainWindow.application.current else: self.application = self.project = self.current = None self._table = None # add the widgets self._setWidgets() if table is not None: self._selectTable(table) elif selectFirstItem: self._modulePulldown.selectFirstItem() def _setWidgets(self): """Set up the widgets for the module """ # make the main splitter self._splitter = Splitter(None, horizontal=False, grid=(0, 0), isFloatWidget=True) self._splitter.setContentsMargins(0, 0, 0, 0) self.mainWidget.getLayout().addWidget(self._splitter, 0, 0) # MUST be inserted this way _topWidget = self._topFrame = Frame(None, setLayout=True, #grid=(0, 0), ) #scrollBarPolicies=('never', 'asNeeded')) _bottomWidget = self._bottomFrame = Frame(None, setLayout=True, #grid=(1, 0), ) #scrollBarPolicies=('never', 'asNeeded')) self._splitter.addWidget(self._topFrame) self._splitter.addWidget(_bottomWidget) self._splitter.setChildrenCollapsible(False) self._splitter.setSizes([1000, 2000]) # add the guiTable to the bottom self._tableWidget = _tableWidget(parent=_bottomWidget, mainWindow=self.mainWindow, moduleParent=self, setLayout=True, grid=(0, 0)) # main widgets at the top row = 0 Spacer(_topWidget, 5, 5, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed, grid=(0, 0), gridSpan=(1, 1)) row += 1 self._modulePulldown = KlassPulldown(parent=_topWidget, mainWindow=self.mainWindow, default=None, grid=(row, 0), gridSpan=(1, 2), minimumWidths=(0, 100), showSelectName=True, sizeAdjustPolicy=QtWidgets.QComboBox.AdjustToContents, callback=self._selectionPulldownCallback, ) # fixed height self._modulePulldown.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) row += 1 HLine(parent=_topWidget, grid=(row, 0), gridSpan=(1, 4), height=16, colour=getColours()[DIVIDER]) row += 1 self.rtWidget = RestraintTablePulldown(parent=_topWidget, mainWindow=self.mainWindow, default=None, grid=(row, 0), gridSpan=(1, 2), minimumWidths=(0, 100), showSelectName=True, sizeAdjustPolicy=QtWidgets.QComboBox.AdjustToContents, callback=self._rtPulldownCallback, ) self.rtWidget.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) row += 1 Label(_topWidget, text='\nmetadata', grid=(row, 0), hAlign='r', vAlign='t') self._metadata = _SimplePandasTableView(_topWidget, showVerticalHeader=False) _topWidget.getLayout().addWidget(self._metadata, row, 1, 1, 3) self._metadata.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) row += 1 self.labelComment = Label(_topWidget, text='comment', grid=(row, 0), hAlign='r') self.lineEditComment = LineEdit(_topWidget, grid=(row, 1), gridSpan=(1, 3), textAlignment='l', backgroundText='> Optional <') self.lineEditComment.editingFinished.connect(self._applyComment) row += 1 Spacer(_topWidget, 5, 5, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed, grid=(row, 3), gridSpan=(1, 1)) _topWidget.getLayout().setColumnStretch(3, 1) def _maximise(self): """ Maximise the attached table """ if self._table: pass else: self.clear() def _closeModule(self): """ CCPN-INTERNAL: used to close the module """ self._tableWidget._close() super()._closeModule() def _selectTable(self, table=None): """ Manually select a ViolationTable from the pullDown """ if not isinstance(table, KlassTable): getLogger().warning(f'select: Object {table} is not of type {KlassTable.className}') return else: for widgetObj in self._modulePulldown.textList: if table.pid == widgetObj: self._table = table self._modulePulldown.select(self._table.pid) def _selectionPulldownCallback(self, item): """ Notifier Callback for selecting violationTable from the pull down menu """ if item is not None: self._table = self.project.getByPid(item) if self._table is not None: self._update(self._table) else: _updateSimplePandasTable(self._tableWidget, pd.DataFrame({})) self.lineEditComment.setText('') _df = pd.DataFrame({'name' : [], 'parameter': []}) _updateSimplePandasTable(self._metadata, _df, _resize=True) def _rtPulldownCallback(self, item): """ Notifier Callback for selecting restraintTable from the pull down menu """ try: with undoBlockWithoutSideBar(): if (_rTable := self.project.getByPid(item)): self._table.setMetadata(_RESTRAINTTABLE, item) else: self._table.setMetadata(_RESTRAINTTABLE, None) except Exception as es: # need to immediately set back to stop error on loseFocus which also fires editingFinished showWarning('Violation Table', str(es)) _df = pd.DataFrame({'name' : self._table.metadata.keys(), 'parameter': self._table.metadata.values()}) _updateSimplePandasTable(self._metadata, _df, _resize=True) def _update(self, table): """ Update the table """ df = table.data if len(table.data) > 0: _updateSimplePandasTable(self._tableWidget, df, _resize=False) else: _updateSimplePandasTable(self._tableWidget, pd.DataFrame({})) _rTablePid = table.getMetadata(_RESTRAINTTABLE) self.rtWidget.select(_rTablePid) self.lineEditComment.setText(table.comment if table.comment else '') _df = pd.DataFrame({'name' : table.metadata.keys(), 'parameter': table.metadata.values()}) _updateSimplePandasTable(self._metadata, _df, _resize=True) def _applyComment(self): """Set the values in the violationTable """ if self._table: comment = self.lineEditComment.text() try: with undoBlockWithoutSideBar(): self._table.comment = comment except Exception as es: # need to immediately set back to stop error on loseFocus which also fires editingFinished showWarning('Data Table', str(es))
#========================================================================================= # _tableWidget #========================================================================================= class _tableWidget(_SimplePandasTableView): """ Class to present a ViolationTable """ className = '_tableWidget' attributeName = KlassTable._pluralLinkName def __init__(self, parent=None, mainWindow=None, moduleParent=None, **kwds): """ Initialise the widgets for the module. """ # Derive application, project, and current from mainWindow self.mainWindow = mainWindow if 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 kwds['setLayout'] = True # Initialise the scroll widget and common settings self._initTableCommonWidgets(parent, **kwds) # initialise the currently attached dataFrame self._hiddenColumns = [] self.dataFrameObject = None # initialise the table super().__init__(parent=parent, showHorizontalHeader=True, showVerticalHeader=False, grid=(3, 0), gridSpan=(1, 6)) self.moduleParent = moduleParent # Initialise the notifier for processing dropped items self._postInitTableCommonWidgets() def _processDroppedItems(self, data): """ CallBack for Drop events """ pids = data.get('pids', []) self._handleDroppedItems(pids, KlassTable, self.moduleParent._modulePulldown) def _close(self): """ Cleanup the notifiers when the window is closed """ pass def _handleDroppedItems(self, pids, objType, pulldown): """ :param pids: the selected objects pids :param objType: the instance of the obj to handle, E.g. PeakList :param pulldown: the pulldown of the module wich updates the table :return: Actions: Select the dropped item on the table or/and open a new modules if multiple drops. If multiple different obj instances, then asks first. """ from ccpn.ui.gui.lib.MenuActions import _openItemObject objs = [self.project.getByPid(pid) for pid in pids] selectableObjects = [obj for obj in objs if isinstance(obj, objType)] others = [obj for obj in objs if not isinstance(obj, objType)] if len(selectableObjects) > 0: _openItemObject(self.mainWindow, selectableObjects[1:]) pulldown.select(selectableObjects[0].pid) else: from ccpn.ui.gui.widgets.MessageDialog import showYesNo othersClassNames = list(set([obj.className for obj in others if hasattr(obj, 'className')])) if len(othersClassNames) > 0: if len(othersClassNames) == 1: title, msg = 'Dropped wrong item.', 'Do you want to open the %s in a new module?' % ''.join(othersClassNames) else: title, msg = 'Dropped wrong items.', 'Do you want to open items in new modules?' openNew = showYesNo(title, msg) if openNew: _openItemObject(self.mainWindow, others) #========================================================================================= # main #=========================================================================================
[docs]def main(): """Show the dataTableModule """ from ccpn.ui.gui.widgets.Application import newTestApplication from ccpn.framework.Application import getApplication # create a new test application app = newTestApplication(interface='Gui') application = getApplication() mainWindow = application.ui.mainWindow # add a module _module = ViolationTableModule(mainWindow=mainWindow) mainWindow.moduleArea.addModule(_module) # show the mainWindow app.start()
if __name__ == '__main__': """Call the test function """ main()