""" qtgui/CCP4ModelWidgets.py: CCP4 Gui Project Copyright (C) 2010 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ ##@package CCP4ModelWidgets (QtGui) Collection of widgets for model data types from PyQt4 import QtGui,QtCore from core import CCP4ModelData from core.CCP4ErrorHandling import * from qtgui import CCP4Widgets class CResidueRangeView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CResidueRange ERROR_CODES = { } def __init__(self,parent=None,model=None,qualifiers={}): qualis = qualifiers CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) #if self.editable: self.connect(self.iconButton,QtCore.SIGNAL('acceptDropData'),self.handleDrop) if model is not None: self.setModel(model) self.layout().addWidget(QtGui.QLabel('Chain')) if self.editable: self.widgets['chainId'] = CCP4Widgets.CComboBox(self,charWidth=2,dragType=self.dragType()) else: self.widgets['chainId'] = CCP4Widgets.CLabel(self,charWidth=4,uneditable=True,dragType=self.dragType()) self.layout().addWidget(self.widgets['chainId']) for item,label in [['firstRes','residue'],['lastRes','to']]: self.layout().addWidget(QtGui.QLabel(label)) if self.editable: self.widgets[item] = CCP4Widgets.CLineEdit(self,charWidth=10,dragType=self.dragType()) else: self.widgets[item] = CCP4Widgets.CLabel(self,charWidth=10,uneditable=True,dragType=self.dragType()) self.layout().addWidget(self.widgets[item]) if self.editable: for item in self.widgets.keys(): self.connect(self.widgets[item],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.connect(self.widgets[item],QtCore.SIGNAL('acceptDropData'),self.acceptDropData) pdbFileObj = self.model.parent().getDataByKey('pdbFileKey') if pdbFileObj is not None: self.loadChains() self.connect(pdbFileObj,QtCore.SIGNAL('dataChanged'),self.loadChains) self.layout().addStretch(5) def loadChains(self): self.widgets['chainId'].clear() for chn in self.model.parent().getChains(): self.widgets['chainId'].addItem(chn) def getMenuDef(self): return ['clear','copy','paste','help'] class CSequenceEdit: def __init__(self,parent=None,model=None): pass class CSequenceView(CCP4Widgets.CComplexLineWidget): ERROR_CODES = { } MODEL_CLASS = CCP4ModelData.CSequence def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'gridLayout':True} qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) if model is not None: self.setModel(model) #if qualis.get('stackButton',None) is not None: # self.layout().addWidget(qualis['stackButton'],0,1) self.layout().addWidget(QtGui.QLabel('Description:',self),1,0) if self.editable: self.widgets['identifier'] = CCP4Widgets.CLineEdit(self,qualifiers=qualis) self.widgets['reference'] = CCP4Widgets.CLineEdit(self,qualifiers=qualis) self.widgets['referenceDb'] = CCP4Widgets.CComboBox(self,qualifiers= CCP4ModelData.CSequence.CONTENTS['referenceDb']['qualifiers'] ) else: self.widgets['identifier'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.widgets['reference'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.widgets['referenceDb'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.widgets['identifier'].setToolTip('Name of the sequence') self.widgets['reference'].setToolTip('Database identifier') self.widgets['referenceDb'].setToolTip('Sequence database') self.layout().addWidget(self.widgets['identifier'],1,1,1,2) self.layout().addWidget(QtGui.QLabel('Database reference:',self),0,0) self.layout().addWidget(self.widgets['referenceDb'],0,1) self.layout().addWidget(self.widgets['reference'],0,2) #if self.editable: # self.widgets['moleculeType']=CCP4Widgets.CComboBox(self,qualifiers= CCP4ModelData.CSequence.CONTENTS['moleculeType']['qualifiers']) #else: # self.widgets['moleculeType']=CCP4Widgets.CLabel(self,qualifiers=qualis) #self.layout().addWidget(self.widgets['moleculeType'],0,5) self.widgets['sequence'] = CCP4Widgets.CTextEdit(self,qualifiers=qualis) self.layout().addWidget(self.widgets['sequence'],2,0,1,3) for item in CCP4ModelData.CSequence.CONTENTS_ORDER: widget = self.widgets.get(item,None) if widget is not None: #print 'CSequenceView.__init__',item,repr(self.model.getDataObjects(item)),type(self.model.getDataObjects(item)) if model is not None: tT = self.model.getDataObjects(item).qualifiers('toolTip') if tT is NotImplemented: tT = '' widget.setToolTip(tT) if self.editable: self.connect(widget,QtCore.SIGNAL(widget.editSignal()),self.updateModelFromView) self.connect(widget,QtCore.SIGNAL('acceptDropData'),self.acceptDropData) def getMenuDef(self): menu = CCP4Widgets.CComplexLineWidget.getMenuDef(self) menu.insert(0,'content') return menu def updateViewFromModel(self): #print 'CSequenceView.updateViewFromModel model',self.model #print 'CSequenceView.updateViewFromModel',self.model.get() if len(self.widgets) == 0: return if self.model is None: for item in ['identifier','reference','referenceDb','sequence']: self.widgets[item].setValue('') else: for item in ['identifier','reference','referenceDb','sequence']: #print 'CSequenceView.updateViewFromModel',item,self.model.__dict__['_value'][item].__str__() self.widgets[item].setValue(self.model.__dict__['_value'][item].__str__()) self.validate() class CSeqDataFileView(CCP4Widgets.CDataFileView): MODEL_CLASS = CCP4ModelData.CSeqDataFile def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'vboxLayout':True} qualis.update(qualifiers) self.enableEdit = qualifiers.get('enableEdit',True) CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis) qualis['iconButton'] = False if model is not None: self.widgets['sequence'] = CSequenceView(parent=parent,model=model.getFileContent(),qualifiers=qualis) else: self.widgets['sequence'] = CSequenceView(parent=parent,qualifiers=qualis) self.layout().addWidget(self.widgets['sequence']) self.widgets['sequence'].hide() self.setModel(model) parentTask = self.parentTaskWidget() if parentTask is not None: self.connect(parentTask,QtCore.SIGNAL('doFix'),self.saveSequence) def getMenuDef(self): if self.editable: menu = ['clear',['View','view_text'],'sep','copy','paste','help'] if self.enableEdit: menu.insert(2,'edit') else: menu = [['View','view_text'],'sep','copy','editLabel','export','help'] return menu def getActionDef(self,name): if name == 'edit': return dict ( text = self.tr("View/edit sequence"), tip = self.tr('You can cut-n-paste sequence into the widget'), slot = self.showEditor ) else: return CCP4Widgets.CDataFileView.getActionDef(self,name) def showEditor(self,visible=None): if visible is None: visible = not self.widgets['sequence'].isVisible() #print 'CSeqDataFile.showEditor',visible,self.model.exists(),self.widgets['sequence'].model if visible and self.model.exists(): self.model.loadFile() #print 'CSeqDataFile.showEditor after loadFile',repr(self.widgets['sequence'].model),self.widgets['sequence'].model if self.widgets['sequence'].model is None: self.widgets['sequence'].setModel(model.fileContent) self.widgets['sequence'].updateViewFromModel() self.widgets['sequence'].setVisible(visible) def setModel(self,model): CCP4Widgets.CDataFileView.setModel(self,model) if self.widgets.get('sequence',None) is None: return if model is not None: if hasattr(model,'fileContent'): self.widgets['sequence'].setModel(model.fileContent) self.connect(model,QtCore.SIGNAL('dataChanged'),self.loadSequence) self.loadSequence() else: self.widgets['sequence'].setModel(None) def loadSequence(self): if not self.editable: return if self.model.isSet(): try: self.model.loadFile() except: self.model.fileContent.unSet() else: self.model.fileContent.unSet() self.updateViewFromModel() #self.widgets['sequence'].updateViewFromModel() def saveSequence(self): widgetValue = self.widgets['sequence'].widgets['sequence'].getValue() #print 'CSeqDataFileView.saveSequence',widgetValue if widgetValue is None or len(widgetValue.strip())==0: return jobId = self.parentTaskWidget().jobId() self.widgets['sequence'].updateModelFromView() if not self.model.baseName.isSet(): self.model.saveSequence(jobId=jobId) self.validate() else: if self.model.fileContent.sequence.isSet(): # User has a filename set and a sequence set - test if they match fileContent = CCP4ModelData.CSequence() #try: if 1: fileContent.loadFile(str(self.model),format=self.model.__dict__['format']) err = self.model.fileContent.assertSame(fileContent) #print 'CSeqDataFileView.saveSequence',self.model.fileContent,fileContent,err.report() if err.maxSeverity()>SEVERITY_WARNING: mess = QtGui.QMessageBox(self) mess.setWindowTitle('Sequence file') mess.setText('Save sequence or reference data to a new file') mess.addButton('Save to file', QtGui.QMessageBox.AcceptRole) mess.addButton('Keep existing file',QtGui.QMessageBox.ActionRole) mess.addButton('Cancel', QtGui.QMessageBox.RejectRole) mess.show() ret = mess.exec_() if ret == QtGui.QMessageBox.RejectRole: return elif ret == QtGui.QMessageBox.ActionRole: self.model.loadFile() else: self.model.saveSequence(jobId=jobId) self.validate() def validate(self,isValid=None,reportMessage=True): dataFileIsValid = CCP4Widgets.CDataFileView.validate(self,isValid=isValid,reportMessage=reportMessage) if self.editable: try: sequenceIsValid = self.widgets['sequence'].validate(isValid=isValid,reportMessage=reportMessage) if self.model.qualifiers('allowUndefined') and sequenceIsValid.maxSeverity()0 and self.model.fileContent.__dict__['loadWarning'][0]['code'] == 108: from core import CCP4Utils win = self.makeImportReport('Importing '+str(filename),self.acceptFixedPir) label = QtGui.QLabel(self) label.setText( 'The input file is not correct PIR format but has been fixed and is shown below.\nIf this is still incorrect please correct the file and select it again.\n' + CCP4ModelData.PIR_DESCRIPTION + '\nYour corrected file:') #label.setReadOnly(True) win.layout().insertWidget(0,label) label = QtGui.QTextEdit(self) label.setText(CCP4Utils.readFile(self.model.fileContent.__dict__['loadWarning'][0]['details'])) label.setReadOnly(True) win.layout().insertWidget(1,label) win.show() return elif self.model.fileContent.__dict__['loadWarning'].maxSeverity()>SEVERITY_WARNING: self.model.fileContent.__dict__['loadWarning'].warningMessage(windowTitle='Importing sequence file',parent=self,message='Failed importing sequence file') self.model.unSet() self.updateViewFromModel() return elif len(self.model.__dict__.get('identifiers',[]))>1: #print 'handleBrowserOpenFile drawing idChooser',getattr(self,'idChooser',None) if getattr(self,'idChooser',None) is None: self.idChooser = QtGui.QListWidget(self) self.idChooser.setSelectionMode(QtGui.QListWidget.SingleSelection) win = self.makeImportReport('Importing '+str(filename),self.handleIdChooser) win.layout().insertWidget(0,self.idChooser) win.layout().insertWidget(0,QtGui.QLabel('Select one of the sequences in the file',self)) self.idChooser.clear() for item in self.model.__dict__['identifiers']: self.idChooser.addItem(item) self.idChooser.window().show() self.idChooser.window().raise_() return if self.model.__dict__['format'] is None or self.model.__dict__['format'] != 'internal': #self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber()) self.doImportFile() else: self.model.emitDataChanged() # No problems just display it! self.updateViewFromModel() self.validate() def makeImportReport(self,title,callBack=None): win = QtGui.QDialog(self) win.setWindowTitle(title) win.setLayout(QtGui.QVBoxLayout()) line = QtGui.QHBoxLayout() box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) win.layout().addLayout(line) line.addStretch(1) line.addWidget(box) line.addStretch(1) if callBack is not None: self.connect(box,QtCore.SIGNAL('clicked ( QAbstractButton * )'),callBack) #self.connect(box,QtCore.SIGNAL('clicked ( QAbstractButton * )'),win.close) return win def doImportFile(self,label=None): if self.model.fileContent.identifier.isSet() and len(self.model.fileContent.identifier)>0: anno = self.model.fileContent.identifier elif label is not None: anno = label+' imported from '+ str(self.model.baseName) else: anno = self.model.qualifiers('guiLabel')+' imported from '+ str(self.model.baseName) #print 'doImportFile anno',anno,'label',label #self.model.blockSignals(True) validatedFile=self.model.fileContent.__dict__.get('validatedFile',None) self.model.importFile(jobId=self.parentTaskWidget().jobId(),annotation=anno,validatedFile=validatedFile,jobNumber=self.parentTaskWidget().jobNumber()) self.model.annotation = anno #self.model.blockSignals(False) self.updateViewFromModel() from core import CCP4Modules if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT and not self.model.dbFileId.isSet(): self.openInfo(label=self.model.qualifiers('guiLabel').lower(),sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation','')) def acceptFixedPir(self,but): #print 'CSeqDataFileView.acceptFixedPir',but.text().__str__() if but.text().__str__() == 'Cancel': self.model.unSet() self.updateViewFromModel() else: self.doImportFile() win = but.window() win.close() self.updateViewFromModel() def handleIdChooser(self,but): #print 'CSeqDataFileView.handleIdChooser', but.text().__str__() record = self.idChooser.currentRow() win = but.window() win.setModal(False) win.close() if but.text().__str__() == 'Cancel': self.model.unSet() self.updateViewFromModel() return else: #print 'CSeqDataFileView.handleIdChooser',record; import sys; sys.stdout.flush() self.model.fileContent.loadExternalFile(self.model.__str__(),self.model.__dict__['format'],record=record) self.doImportFile() print 'handleIdChooser',self.model.fileContent.identifier,self.model.fileContent.sequence self.updateViewFromModel() def filterText(self): # make the filters text for QFileDialog to include the alignment extensions textList = [] for desc,extList in [ [CCP4ModelData.CSeqDataFile.QUALIFIERS['mimeTypeDescription'] , CCP4ModelData.EXTLIST ], ]: text = desc + ' (' for ext in extList.keys(): text = text + '*.'+ext+' ' textList.append( text[0:-1]+')' ) return textList ''' This reimplemetation of CDataFileView provides tools for user to merge Refmac dictionary files It is no longer presented on the gui but the issues are handled in the appropriate scripts eg.CPluginScript.mergeDictToProjectLib() is used. class CDictDataFileView(CCP4Widgets.CDataFileView): MODEL_CLASS = CCP4ModelData.CDictDataFile PROJECT_FILE_LABEL = 'Ideal ligand geometry file for project' def __init__(self,parent=None,model=None,qualifiers={},**kw): qualis = {} qualis.update(qualifiers) qualis.update(kw) CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis) def getActionDef(self,name): if name == 'manage_dict': return dict ( text = self.tr("View/edit dictionary"), tip = self.tr('View/edit the currently selected dictionary'), slot = self.manageProjectDictionary ) else: return CCP4Widgets.CDataFileView.getActionDef(self,name) def manageProjectDictionary(self): if not hasattr(self,'dictManager'): #self.dictManager = CDictDataDialog(model=self.model.fileContent) self.dictManager = CDictDataDialog(parent=self.parentTaskWidget(),projectId=self.parentTaskWidget().projectId()) self.dictManager.show() self.dictManager.raise_() def handleMerge(self): from qtgui import CCP4FileBrowser from core import CCP4Modules self.mergeFileBrowser = CCP4FileBrowser.CFileDialog(parent=self,title='Select dictionary file to merge into project dictionary', defaultSuffix=CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo(name='application/refmac-dictionary',info='fileExtensions'), fileMode=QtGui.QFileDialog.ExistingFiles,saveButtonText='Merge these files') self.mergeFileBrowser.show() self.connect(self.mergeFileBrowser,QtCore.SIGNAL('selectFiles'),self.mergeFiles) def mergeFiles(self,selectedFiles=[]): # 'CDictDataFileView.mergeFiles',selectedFiles if hasattr(self,'mergeFileBrowser'): self.mergeFileBrowser.close() self.mergeFileBrowser.deleteLater() from core import CCP4Modules workDirectory = CCP4Modules.PROJECTSMANAGER().jobDirectory( jobId=self.parentTaskWidget().jobId(),projectId=self.parentTaskWidget().projectId()) newFile,err = self.model.mergeInDictFiles(dictFileList=selectedFiles,parentWorkDirectory=workDirectory) for fileName in selectedFiles: err = self.model.fileContent.mergeFile(fileName=fileName,overwrite=True) #print 'CDictDataFileView.mergeFiles',err.report(),err.maxSeverity() if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage(windowTitle='Error merging Refmac dictionaries',parent=self,message='Error attempting to merge Refmac dictionaries') return """ def loadJobCombo(self): CCP4Widgets.CDataFileView.loadJobCombo(self) if self.jobCombo.findText(CDictDataFileView.PROJECT_FILE_LABEL)<0: # Call the defaultProjectDict even though result is not used it will ensure that # a file exists (by creting emptly file if necessary) if self.model is None: return dictFile = self.model.defaultProjectDict(projectId=self.parentTaskWidget().projectId()) self.jobCombo.insertItem(1,CDictDataFileView.PROJECT_FILE_LABEL) self.jobCombo.setCurrentIndex(1) """ def handleFollowFrom(self,contextJobId,projectId): self.jobCombo.setCurrentIndex(1) def handleJobComboChange(self,indx=None): self.connectUpdateViewFromModel(False) if indx is None: indx = 0 if str(self.jobCombo.itemText(indx)) == CDictDataFileView.PROJECT_FILE_LABEL: self.model.set(self.model.defaultProjectDict(projectId=self.parentTaskWidget().projectId())) self.model.annotation = CDictDataFileView.PROJECT_FILE_LABEL else: CCP4Widgets.CDataFileView.handleJobComboChange(self,indx0=indx) ''' class CMonomerView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CMonomer def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'gridLayout':True} qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) if model is not None: self.setModel(model) self.layout().addWidget(QtGui.QLabel('Identifier:',self),0,1) if self.editable: self.widgets['identifier'] = CCP4Widgets.CStringView(self,qualifiers=qualis) else: self.widgets['identifier'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.layout().addWidget(self.widgets['identifier'],0,2) self.layout().addWidget(QtGui.QLabel('Formula:',self),0,3) if self.editable: self.widgets['formula'] = CCP4Widgets.CStringView(self,qualifiers=qualis) else: self.widgets['formula'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.layout().addWidget(self.widgets['formula'],0,4) self.layout().addWidget(QtGui.QLabel('Dictionary name:',self),1,1) if self.editable: self.widgets['dictionaryName'] = CCP4Widgets.CStringView(self,qualifiers=qualis) else: self.widgets['dictionaryName'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.layout().addWidget(self.widgets['dictionaryName'],1,2) self.layout().addWidget(QtGui.QLabel('Smiles string:',self),1,3) if self.editable: self.widgets['smiles'] = CCP4Widgets.CStringView(self,qualifiers=qualis) else: self.widgets['smiles'] = CCP4Widgets.CLabel(self,qualifiers=qualis) self.layout().addWidget(self.widgets['smiles'],1,4) ''' toolTip = self.model.qualifiers('toolTip') if toolTip is NotImplemented: toolTip = '' else: toolTip = toolTip for item in self.model.CONTENTS_ORDER: widget = self.widgets[item] tT = self.model.getDataObjects(item).qualifiers('toolTip') if tT is NotImplemented: tT = '' widget.setToolTip(toolTip+tT) if self.editable: self.connect(widget,QtCore.SIGNAL(widget.editSignal()),self.updateModelFromView) self.connect(widget,QtCore.SIGNAL('acceptDropData'),self.acceptDropData) ''' class CPdbEnsembleItemView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CPdbEnsembleItem def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'vboxLayout':True,'iconButton':False} qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) if model is not None: self.setModel(model) self.widgets = {} self.widgets['structure'] = CPdbDataFileView(self,qualifiers={'iconButton':True,'iconName':'PdbDataFile','ifAtomSelection' : True}) self.layout().addWidget(self.widgets['structure']) self.widgets['identity_to_target'] = CCP4Widgets.CFloatView(self) self.widgets['rms_to_target'] = CCP4Widgets.CFloatView(self) self.widgets['number'] = CCP4Widgets.CIntView(self, qualifiers = { 'editable' : self.editable, 'guiMode' : 'combo', 'enumerators' : [i for i in range(21)], 'menuText' : [str(i) for i in range(21)]}) self.widgets['number'].setMaximumWidth(60) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Copies:',self)) line.addWidget(self.widgets['number']) line.addWidget(QtGui.QLabel('Sequence identity:',self)) line.addWidget(self.widgets['identity_to_target']) line.addWidget(QtGui.QLabel('OR RMS difference:',self)) line.addWidget(self.widgets['rms_to_target']) self.layout().addLayout(line) def setModel(self, model): ensemble = None if model is not None and hasattr(model,'parent') and model.parent() is not None: ensemble = model.parent().parent() cEnsembleLabelView = self.parent().findChildren(CEnsembleLabelView)[0] if self.model is not None: for item in ['structure','identity_to_target','rms_to_target']: self.disconnect(self.model.get(item),QtCore.SIGNAL('dataChanged'),self.validate) if ensemble is not None: for item in ['number']: self.disconnect(ensemble.get(item),QtCore.SIGNAL('dataChanged'),self.numberChanged) if model is None or isinstance(model,self.MODEL_CLASS): from qtgui.CCP4Widgets import CViewWidget CViewWidget.setModel(self,model) if model is not None: self.connect(model,QtCore.SIGNAL('dataChanged'),self.validate) toolTip = model.qualifiers('toolTip') if toolTip is not NotImplemented and toolTip is not None and self.iconButton is not None: self.iconButton.setToolTip(toolTip+'\n'+self.iconButton.toolTip()) for key,w in self.widgets.items(): if key in ['structure','identity_to_target','rms_to_target']: if isinstance(w,CViewWidget): w.setModel(model.get(key)) elif key in ['number']: if ensemble is not None: if isinstance(w,CViewWidget): w.setModel(ensemble.get(key)) else: for key,w in self.widgets.items(): if isinstance(w,CViewWidget): w.setModel(None) # Set allowUndefined True for the first CPdbEnsembleItem in the first CEnsemble where the CEnsembleList # is allowed zero length if model is not None and ensemble is not None: if isinstance(ensemble,CCP4ModelData.CEnsemble) and ensemble.qualifiers('allowUndefined'): if model.parent().index(model) == 0: model.setQualifier('allowUndefined',True) model.structure.setQualifier('allowUndefined',True) if model is not None and self.editable: for item in ['structure','identity_to_target','rms_to_target']: self.connect(model.get(item),QtCore.SIGNAL('dataChanged'),self.validate) for item in ['number']: if ensemble is not None: self.connect(ensemble.get(item),QtCore.SIGNAL('dataChanged'),self.numberChanged) if self.widgets['structure'].widgets.get('selection',None) is None: self.widgets['structure'].showAtomSelection() def updateViewFromModel(self): for key,widget in self.widgets.items(): widget.updateViewFromModel() #self.parent().findChildren(CEnsembleLabelView)[0].updateViewFromModel() def numberChanged(self, **kw): cEnsembleLabelView = self.parent().findChildren(CEnsembleLabelView)[0] self.parent().parent().updateViewFromModel(**kw) cEnsembleLabelView.validate() ''' def getMenuDef(self): return self.widgets['structure'].getMenuDef() def getActionDef(self,name): return self.widgets['structure'].getActionDef(name) def showAtomSelection(self): self.widgets['structure'].showAtomSelection() def viewContents(self): self.widgets['structure'].viewContents() ''' class CEnsembleView(CCP4Widgets.CComplexLineWidget): def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'vboxLayout':True} qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) if model is not None: self.setModel(model) self.widgets = {} line = QtGui.QHBoxLayout() iconWidgetItem = self.layout().takeAt(0) line.addWidget(iconWidgetItem.widget()) line.addWidget(QtGui.QLabel('Label for ensemble:',self)) self.widgets['label'] = CCP4Widgets.CStringView(self) line.addWidget(self.widgets['label']) self.layout().addLayout(line) self.widgets['pdbItemList'] = CCP4Widgets.CListView(self,model=self.model.pdbItemList,qualifiers={ 'editable' : self.editable, 'mode' : 'table', 'tableItems': ['structure','identity_to_target','rms_to_target'], 'columnHeaders':['Filename','Identity','RMS'] }) self.layout().addWidget(self.widgets['pdbItemList']) class CEnsembleLabelView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CEnsemble def __init__(self,parent=None,model=None,qualifiers={}): CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualifiers) self.widgets['use'] = CCP4Widgets.CBooleanView(self, qualifiers = { 'editable' : self.editable } ) self.widgets['number'] = CCP4Widgets.CIntView(self, qualifiers = { 'editable' : self.editable, 'guiMode' : 'combo', 'enumerators' : [i for i in range(21)], 'menuText' : [str(i) for i in range(21)]}) self.widgets['number'].setMaximumWidth(60) self.widgets['label'] = CCP4Widgets.CStringView(self, qualifiers = { 'editable' : self.editable } ) self.layout().addWidget( self.widgets['use'] ) self.layout().addWidget(QtGui.QLabel('Find',self)) self.layout().addWidget( self.widgets['number'] ) self.layout().addWidget(QtGui.QLabel('of search ensemble called',self)) self.layout().addWidget( self.widgets['label'] ) self.setModel(model) def validate(self,isValid=None,reportMessage=True): # Just validate the number and label if isValid is None: if self.model is None: isValid = False else: v = self.model.number.validity(self.model.number.get()) v.extend(self.model.label.validity(self.model.label.get())) isValid = (v.maxSeverity()<=SEVERITY_WARNING) CCP4Widgets.CComplexLineWidget.validate(self,isValid=isValid,reportMessage=reportMessage) class CEnsembleListView(CCP4Widgets.CTreeView): MODEL_CLASS = CCP4ModelData.CEnsembleList def __init__(self,parent=None,model=None,qualifiers={}): displayRole = QtCore.Qt.DisplayRole qualis = { 'editors' : [ { 'modelClass' : CCP4ModelData.CEnsemble, 'name' : 'ensemble', 'label':'ensemble' } , { 'modelClass' : CCP4ModelData.CPdbEnsembleItem , 'name' : 'pdbEnsembleItem','label':'structure in ensemble' } ], 'columnHeaders':[ {displayRole:'Ensemble/Filename','width':240}, {displayRole:'Selection','width':240}, {displayRole:'Identity','width':50}, {displayRole:'RMS','width':50} ] } """ 'hierarchy' : [ {'name':'self','label':'ensemble','editorClass':CEnsembleLabelView, 'grey':True, 'list': [{'name':'pdbItemList','label':'structure in ensemble','editorClass':CPdbEnsembleItemView }] } ], """ #print 'INTO CEnsembleListView',model qualis.update(qualifiers) super(CEnsembleListView,self).__init__(parent,model=model,qualifiers=qualis) self.listWidget.setMinimumHeight(150) class CPdbDataFileView(CCP4Widgets.CDataFileView): MODEL_CLASS = CCP4ModelData.CPdbDataFile def __init__(self,parent=None,model=None,qualifiers={}): self.ifAtomSelection = qualifiers.get('ifAtomSelection',False) qualis = { 'vboxLayout' : True } qualis.update(qualifiers) #print 'CPdbDataFileView parent',parent #CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis) super(CPdbDataFileView,self).__init__(parent=parent,model=model,qualifiers=qualis) #if self.ifAtomSelection: self.showAtomSelection() def getMenuDef(self): if self.editable: #menu = ['clear','view','annotate','sep','copy','paste','help'] menu = ['clear',['View','quick_view','view_text','view_CCP4mg','view_Coot','view_PdbView'],'sep','copy','paste','help'] if self.ifAtomSelection: menu.insert(menu.index('sep'),'select') else: if self.role is not None and self.role == 'output': menu = [['View','quick_view','view_text','view_CCP4mg','view_Coot','view_PdbView'],'sep','copy','editLabel','export','help'] else: menu = [['View','quick_view','view_text','view_CCP4mg','view_Coot','view_PdbView'],'sep','copy','export','help'] if self._stacked: menu.insert(0,'handleStack') if self.ifInfo: menu.insert(menu.index('sep'),'annotate') return menu def getActionDef(self,name): if name == 'select': def iC(): try: return self.widgets['selection'].isVisible() except: return False return dict ( text = self.tr("Select atoms"), tip = self.tr('Select limited set of atoms'), slot = self.showAtomSelection, checkable = True, checked = iC ) elif name == 'quick_view': def e(): return (self.model is not None and self.model.exists()) return dict ( text = self.tr("Quick view"), tip = self.tr('Quick view of model data'), slot = self.viewContents, enabled = e ) else: return CCP4Widgets.CDataFileView.getActionDef(self,name) def openViewer(self,mode): from core import CCP4Modules if mode == 'view_text': CCP4Modules.WEBBROWSER().openFile(fileName=self.model.__str__(),toFront=True) else: CCP4Widgets.CDataFileView.openViewer(self,mode) def setModel(self,model=None): #print 'CPdbDataFileView.setModel',repr(model),self.widgets.has_key('selection') #if model is not None: print model.objectName() if self.model is not None and self.widgets.has_key('selection'): self.disconnect(self.model,QtCore.SIGNAL('dataChanged'),self.widgets['selection'].applySelection) super(CPdbDataFileView,self).setModel(model=model) if self.ifAtomSelection and model is not None: if 'selection' in self.widgets: self.widgets['selection'].setModel(model.selection) self.widgets['selection'].updateViewFromModel() elif model is not None and model.selection.isSet(): if 'selection' not in self.widgets: self.showAtomSelection() self.widgets['selection'].setModel(model.selection) self.widgets['selection'].updateViewFromModel() def getValue(self): val = CCP4Widgets.CDataFileView.getValue(self) if getattr(self,'selectionLine',None) is not None: val['selection'] = self.widgets['selection'].getValue() #print 'CPdbDataFileView.getValue',val return val def setValue(self,value): #print 'CPdbDataFileView.setValue',self.model.objectName(),value CCP4Widgets.CDataFileView.setValue(self,value) if value.get('selection',None) is not None: self.showAtomSelection() self.widgets['selection'].setValue(value.get('selection',{})) elif getattr(self,'selectionLine',None) is not None: self.widgets['selection'].clear() else: return self.widgets['selection'].applySelection() def showAtomSelection(self): #print 'CPdbDataFileView.showAtomSelection' import sys,os from core import CCP4Utils hideSimpleSelections = True if self.widgets.get('selection',None) is None: self.selectionLine = QtGui.QFrame(self) self.selectionLine.setLayout(QtGui.QHBoxLayout()) self.selectionLine.layout().setSpacing(0) self.selectionLine.layout().setContentsMargins(0,0,0,0) self.selectionLine.layout().addWidget(QtGui.QLabel('Atom selection')) selection = None if hasattr(self,'model') and self.model is not None: selection = self.model.selection self.widgets['selection'] = CAtomSelectionView(parent=self,model=selection, qualifiers= { 'editable' :self.editable} ) if hasattr(self,'model') and self.model is not None: self.connect(self.model,QtCore.SIGNAL('dataChanged'),self.widgets['selection'].applySelection) self.selectionLine.layout().addWidget(self.widgets['selection']) self.layout().addWidget(self.selectionLine) self.simpleSelectionsCheck = QtGui.QCheckBox('Simple selections ...') self.layout().addWidget(self.simpleSelectionsCheck) expanded_cb_path = os.path.abspath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','toc-minus.png')) unexpanded_cb_path = os.path.abspath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','toc-plus.png')) expandStyle = """ QCheckBox::indicator:unchecked { image: url("""+unexpanded_cb_path+"""); } QCheckBox::indicator:checked { image: url("""+expanded_cb_path+"""); } """ self.simpleSelectionsCheck.setStyleSheet(expandStyle) self.selectionTypes = QtGui.QFrame(self) self.layout().addWidget(self.selectionTypes) self.chainsWidget = QtGui.QFrame(self) self.layout().addWidget(self.chainsWidget) self.excludeEdit = QtGui.QLineEdit(self) if self.editable: self.selectionTypes.setLayout(QtGui.QGridLayout()) self.chainsWidget.setLayout(QtGui.QGridLayout()) self.selectionTypes.layout().addWidget(QtGui.QLabel('Residue types'),0,0) peptideWidget = QtGui.QCheckBox('Peptide') nucleicWidget = QtGui.QCheckBox('Nucleic acid') ligandWidget = QtGui.QCheckBox('Ligands') waterWidget = QtGui.QCheckBox('Water') soluteWidget = QtGui.QCheckBox('Solute') saccharideWidget = QtGui.QCheckBox('Saccharide') nucleotideWidget = QtGui.QCheckBox('Nucleotide') metalWidget = QtGui.QCheckBox('Metal') def interpretSimpleSelections(): sel = '' if peptideWidget.isChecked(): sel += 'peptide' if nucleicWidget.isChecked(): if len(sel)>0: sel += ' or nucleic' else: sel = 'nucleic' if ligandWidget.isChecked(): if len(sel)>0: sel += ' or ligands' else: sel = 'ligands' if waterWidget.isChecked(): if len(sel)>0: sel += ' or water' else: sel = 'water' if soluteWidget.isChecked(): if len(sel)>0: sel += ' or solute' else: sel = 'solute' if saccharideWidget.isChecked(): if len(sel)>0: sel += ' or saccharide' else: sel = 'saccharide' if nucleotideWidget.isChecked(): if len(sel)>0: sel += ' or nucleotide' else: sel = 'nucleotide' if metalWidget.isChecked(): if len(sel)>0: sel += ' or metal' else: sel = 'metal' chains = self.chainsWidget.findChildren(QtGui.QCheckBox) chainsel = '' for ch in chains: #print ch.isChecked(), ch.text() if ch.isChecked(): if len(chainsel)>0: chainsel += " or " + str(ch.text())+"/" else: chainsel = str(ch.text())+"/" #print chainsel; sys.stdout.flush() if len(chainsel)>0 and len(sel)>0: sel = "{"+sel+"} and {"+chainsel+"}" elif len(sel)==0 and len(chainsel)>0: sel = chainsel if len(str(self.excludeEdit.text()).strip())>0: theElements = str(self.excludeEdit.text()).strip().split(",") theNewElements = [] for ele in theElements: if not ele in theNewElements: theNewElements.append(ele) if ele.upper() != ele: if not ele.upper() in theNewElements: theNewElements.append(ele.upper()) theNewElements = ",".join(theNewElements) if len(sel)>0: sel += ' and {not /*/*/*/*['+theNewElements+']:*}' else: sel = '{not /*/*/*/*['+theNewElements+']:*}' val = {'selection':{'text':sel}} CCP4Widgets.CDataFileView.setValue(self,val) self.widgets['selection'].setValue(val) self.widgets['selection'].applySelection() # Surely should not have to do this? But applySelection0 seems not to work... self.widgets['selection'].connectUpdateViewFromModel(False) self.widgets['selection'].model.text.set(sel) self.widgets['selection'].connectUpdateViewFromModel(True) peptideWidget.stateChanged.connect(interpretSimpleSelections) nucleicWidget.stateChanged.connect(interpretSimpleSelections) ligandWidget.stateChanged.connect(interpretSimpleSelections) waterWidget.stateChanged.connect(interpretSimpleSelections) soluteWidget.stateChanged.connect(interpretSimpleSelections) saccharideWidget.stateChanged.connect(interpretSimpleSelections) nucleotideWidget.stateChanged.connect(interpretSimpleSelections) metalWidget.stateChanged.connect(interpretSimpleSelections) self.selectionTypes.layout().addWidget(peptideWidget,1,0) self.selectionTypes.layout().addWidget(nucleicWidget,2,0) self.selectionTypes.layout().addWidget(ligandWidget,3,0) self.selectionTypes.layout().addWidget(waterWidget,4,0) self.selectionTypes.layout().addWidget(soluteWidget,1,1) self.selectionTypes.layout().addWidget(saccharideWidget,2,1) self.selectionTypes.layout().addWidget(metalWidget,3,1) self.selectionTypes.layout().addWidget(nucleotideWidget,4,1) def fillChainsWidget(): #print "fillChainsWidget"; sys.stdout.flush() if hasattr(self,"model") and hasattr(self.model,"fileContent") and hasattr(self.model.fileContent,"composition") and hasattr(self.model.fileContent.composition,"chains"): if len(self.model.fileContent.composition.chains)<30: # and len(self.model.fileContent.composition.chains)>1: self.chainsWidget.layout().addWidget(QtGui.QLabel('Chains'),0,0) for i in range(len(self.model.fileContent.composition.chains)): col = i / 8 row = i % 8 + 1 chainWidget = QtGui.QCheckBox(self.model.fileContent.composition.chains[i]) self.chainsWidget.layout().addWidget(chainWidget,row,col) chainWidget.stateChanged.connect(interpretSimpleSelections) def fillExcludeWidget(): self.excludeLabel = QtGui.QLabel('Exclude (comma separated) atoms of type:') self.excludeEdit.setToolTip("Comma separated list of atoms to not include, e.g. H,C,N,Ag") #This line adds a blank middle column which is somewhat more visually appealing (to SJM) than the long text entry box. self.chainsWidget.layout().setColumnStretch(1,1) self.chainsWidget.layout().addWidget(self.excludeLabel,0,2) self.chainsWidget.layout().addWidget(self.excludeEdit,1,2) self.excludeEdit.textEdited.connect(interpretSimpleSelections) fillChainsWidget() fillExcludeWidget() def clearLayout(layout): #print "clearLayout"; sys.stdout.flush() #print self.model.fileContent.molHnd while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() def dataIsChanged(): #print "dataIsChanged"; sys.stdout.flush() if not self.model.fileContent.molHnd is self._old_molHnd: clearLayout(self.chainsWidget.layout()) fillChainsWidget() self.excludeEdit = QtGui.QLineEdit(self) fillExcludeWidget() self._old_molHnd = self.model.fileContent.molHnd if hasattr(self,"model") and hasattr(self.model,"fileContent") and hasattr(self.model.fileContent,"composition") and hasattr(self.model.fileContent.composition,"chains"): self._old_molHnd = self.model.fileContent.molHnd else: self._old_molHnd = None self.connect(self.model,QtCore.SIGNAL('dataChanged'),dataIsChanged) elif not self.widgets['selection'].isVisible():# and self.simpleSelectionsCheck.isChecked(): self.selectionLine.show() if hasattr(self,"simpleSelectionsCheck"): self.simpleSelectionsCheck.show() if self.simpleSelectionsCheck.isChecked(): hideSimpleSelections = False self.selectionTypes.show() self.chainsWidget.show() else: self.selectionLine.hide() self.selectionTypes.hide() self.chainsWidget.hide() if hasattr(self,"simpleSelectionsCheck"): self.simpleSelectionsCheck.hide() if not self.editable: if hasattr(self,"excludeEdit"): self.excludeEdit.hide() if hasattr(self,"excludeLabel"): self.excludeLabel.hide() self.selectionTypes.hide() self.chainsWidget.hide() self.simpleSelectionsCheck.hide() self.simpleSelectionsCheck.clicked.connect(self.selectionTypes.setVisible) self.simpleSelectionsCheck.clicked.connect(self.chainsWidget.setVisible) if hideSimpleSelections: self.selectionTypes.hide() self.chainsWidget.hide() def viewContents(self): if not hasattr(self,'contentsViewer'): self.contentsViewer = CPdbContentsViewer(self) try: fileContent = self.model.fileContent except: pass self.connect(self.model.fileContent,QtCore.SIGNAL('dataChanged'),self.contentsViewer.load) self.contentsViewer.load() self.contentsViewer.show() def setFocus1(self,reason,index=None): #print 'CPdbDataFileView.setFocus1',index if index is None: CCP4Widgets.CDataFileView.setFocus(self,reason) elif index==1 and self.ifAtomSelection: self.showAtomSelection() self.selectionLine.setFocus(reason) else: self.jobCombo.setFocus(reason) def handleBrowserOpenFile(self,filename,downloadInfo,**kw): kw['validate'] = False kw['updateView'] = False CCP4Widgets.CDataFileView.handleBrowserOpenFile(self,filename,downloadInfo,**kw) err = self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber()) if err is not None and len(err)>0: if err.maxSeverity()>SEVERITY_WARNING: message = 'File failed validation test' err.warningMessage(windowTitle='Importing coordinate file',parent=self,message=message) self.model.unSet() self.updateViewFromModel() self.validate() class CPdbContentsViewer(QtGui.QDialog): def __init__(self,parent): QtGui.QDialog.__init__(self,parent) self.setLayout(QtGui.QVBoxLayout()) self.textEdit = QtGui.QTextEdit(self) self.textEdit.setReadOnly(True) self.layout().addWidget(self.textEdit) butBox = QtGui.QDialogButtonBox(self) self.layout().addWidget(butBox) but = butBox.addButton(QtGui.QDialogButtonBox.Close) self.connect(but,QtCore.SIGNAL('released()'),self.close) def load(self): #print 'CPdbContentsViewer.load', self.parent().model.fileContent.composition.chains self.textEdit.setReadOnly(False) self.textEdit.clear() if self.parent().model.annotation.isSet(): self.setWindowTitle(self.parent().model.annotation.__str__()) else: self.setWindowTitle(self.parent().model.__str__()) text = '\nSpace group: ' + str(self.parent().model.fileContent.mmdbManager.GetSpaceGroup()) try: cell = self.parent().model.fileContent.mmdbManager.GetCell() text = text + '\nCell:' for ii in range(1,7): text = text + ' ' + str(cell[ii]) except: pass if len( self.parent().model.fileContent.composition.chains)>0: text = text + '\n\nChains in model:\n' comp = self.parent().model.fileContent.composition for i in range(len(comp.chains)): text = text + str(comp.chains[i])+ ' ' + comp.chainInfo[i][1].split('/')[3] + ' - ' + comp.chainInfo[i][2].split('/')[3] + \ ' ('+str(comp.chainInfo[i][0]) +' residues)\n' if len( self.parent().model.fileContent.composition.monomers)>0: text = text + '\n\nMonomers in model:\n'+self.parent().model.fileContent.composition.monomers[0] for mon in self.parent().model.fileContent.composition.monomers[1:]: text = text + ', ' + str(mon) self.textEdit.document().setPlainText(text) self.textEdit.setReadOnly(True) class CPdbDataFileListView(CCP4Widgets.CListView): MODEL_CLASS = CCP4ModelData.CPdbDataFileList def __init__(self,parent=None,model=None,qualifiers={}): #print 'CPdbDataFileListView' qualis = { 'mode' : 'table', 'tableItems' : ['fullPath','selection'] , 'columnHeaders':['Coordinate file','Selection'], } qualis.update(qualifiers) CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis) # Tis broke! self.editor.showAtomSelection() class CAtomSelectionView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CAtomSelection ERROR_CODES = { 101 : { 'description' : 'No CPdbDataFile when applying atom selection' }, 102 : { 'description' : 'Error applying atom selection' } } def __init__(self,parent=None,model=None,qualifiers={}): super(CAtomSelectionView,self).__init__(parent=parent,qualifiers=qualifiers) if self.editable: self.widgets['text'] = CCP4Widgets.CLineEdit(self) self.widgets['text'].setToolTip("Enter selection command e.g. 'A/ or B/11-21'") self.setFocusProxy(self.widgets['text']) else: self.widgets['text'] = CCP4Widgets.CLabel(self) self.layout().addWidget(self.widgets['text']) self.label = QtGui.QLabel(' ( 0 atoms)',self) self.layout().addWidget(self.label) self.help = QtGui.QPushButton('Help',self) self.help.setToolTip('Details of atom selection syntax') self.layout().addWidget(self.help) self.connect(self.widgets['text'],QtCore.SIGNAL('textEdited(const QString &)'),self.applySelection0) self.connect(self.help,QtCore.SIGNAL('released()'),self.showHelp) if self.parent().model is not None: self.connect(self.parent().model,QtCore.SIGNAL('dataLoaded'),self.applySelection) if model is not None: self.setModel(model) self.updateViewFromModel() def validate(self,isValid=None,reportMessage=True): pass def showHelp(self): from core import CCP4Modules CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='general/atom_selection.html') def setValue(self,value={}): CCP4Widgets.CComplexLineWidget.setValue(self,value=value) try: self.applySelection() except: self.label.setText('No atoms') def updateViewFromModel(self): #print 'CAtomSelectionView.updateViewFromModel' #import traceback #traceback.print_stack(limit=10) self.widgets['text'].blockSignals(True) self.widgets['text'].setText(self.model.__str__()) self.widgets['text'].blockSignals(False) try: self.applySelection() except: self.label.setText('No atoms') def clear(self): CCP4Widgets.CComplexLineWidget.clear(self) self.widgets['text'].clear() self.label.setText('No atoms') def applySelection0(self,text=None): try: self.applySelection(text=text) except: pass else: self.connectUpdateViewFromModel(False) self.model.text.set(text) self.connectUpdateViewFromModel(True) def applySelection(self,text=None): #print 'CSelectionLine.applySelection',text #import traceback #traceback.print_stack(limit=5) if text is None: text = self.widgets['text'].text() if isinstance(self.model.parent(),CCP4ModelData.CPdbDataFile): pdbDataObj = self.model.parent() else: pdbDataObj = self.model.getDataByKey('pdbFileKey') #print 'CSelectionLine.applySelection',text,pdbDataObj if pdbDataObj is None: try: pdbDataObj = self.model.parent() except: self.label.setText('No atoms') raise CException(self.__class__,101,name=self.modelObjectPath()) try: # Call to loadFile removed from here - CPdbDataFile.updateData() unsets fileContent # so this accessing fileContent should cause a loadFile() if necessasy nSelAtoms,selHnd = pdbDataObj.fileContent.interpretSelection(str(text)) if selHnd is not None: pdbDataObj.fileContent.molHnd.DeleteSelection(selHnd) except Exception as e: #print 'CSelectionLine.applySelection loadFile fail\n',e self.label.setText('No atoms') raise CException(self.__class__,102,name=self.modelObjectPath()) self.label.setText(' ( '+str(nSelAtoms)+' atoms)') if nSelAtoms == 0: self.setProperty("isValid",False) self.setProperty("hasWarning",True) self.isValid = False self.validityMessage = 'No atoms selected' else: self.setProperty("isValid",True) self.setProperty("hasWarning",False) self.isValid = True self.validityMessage = None self.updateValidityIndicator() class CDictDataDialog(QtGui.QDialog): def __init__(self,parent=None,model=None,projectId=None): from core import CCP4Modules if parent is None: parent = CCP4Modules.QTAPPLICATION() QtGui.QDialog. __init__(self,parent) self.setLayout(QtGui.QHBoxLayout()) self.setModal(False) if projectId is not None: pName = ' for '+CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectname') else: pName='' self.setWindowTitle('Manage project dictionary'+pName) self.frame = CDictDataView(self,model=model,projectId=projectId) self.layout().addWidget(self.frame) def closeEvent(self,event): from core import CCP4Utils,CCP4Modules #print 'CDictDataDialog.closeEvent',self.frame.showWidget if self.frame.showWidget is not None and CCP4Utils.isAlive(self.frame.showWidget): CCP4Modules.WEBBROWSER().tab().deleteTabWidget(widget=self.frame.showWidget) event.accept() class CDictDataView(CCP4Widgets.CViewWidget): def __init__(self,parent=None,model=None,projectId=None): CCP4Widgets.CViewWidget.__init__(self,parent=parent) self.showWidget = None if model is None: self.dictDataFile = CCP4ModelData.CDictDataFile() #self.dictDataFile.set(self.dictDataFile.defaultProjectDict(projectId=projectId)) self.dictDataFile.loadFile() self.model = self.dictDataFile.fileContent from qtgui import CCP4ProjectViewer import os CCP4ProjectViewer.FILEWATCHER().addJobPath(os.path.split( os.path.split(str(self.dictDataFile))[0])[1],self.dictDataFile.__str__()) self.connect(CCP4ProjectViewer.FILEWATCHER(),QtCore.SIGNAL('fileChanged(const QString &)'),self.handleFileChanged) else: self.model = model self.dictDataFile = model.parent() #print 'CDictDataView',self.dictDataFile,self.model,self.model.monomerList QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QHBoxLayout()) self.layout().setContentsMargins(1,1,1,1) self.layout().setSpacing(1) self.monomerListWidget = CDictDataList(self) self.layout().addWidget(self.monomerListWidget ) butLayout = QtGui.QVBoxLayout() for label,connect in [['Show',self.handleShow],['Import geometry file',self.handleMerge],['Delete',self.handleDelete]]: but = QtGui.QPushButton(label,self) self.connect(but,QtCore.SIGNAL('released()'),connect) butLayout.addWidget(but) self.layout().addLayout(butLayout) self.updateViewFromModel() self.connect(self.model,QtCore.SIGNAL('dataChanged'),self.updateViewFromModel) self.connect(self.monomerListWidget,QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *,int)'),self.handlDoubleClick) def handleFileChanged(self,path): #print 'CDictDataView.handleFileChanged',path if str(path) == str(self.dictDataFile): #print 'CDictDataView.handleFileChanged updating' self.dictDataFile.fileContent.loadFile(str(self.dictDataFile)) self.updateViewFromModel() def handlDoubleClick(self,item,col): #print 'CDictDataView.handlDoubleClick',item,col idd = item.data(0).toStrint().__str__() self.handleShow(idd) def updateViewFromModel(self): from core import CCP4Utils self.monomerListWidget.load(self.model.monomerList) if self.showWidget is not None and CCP4Utils.isAlive(self.showWidget): self.showWidget.reload() def updateModelFromView(self): # All updates should be handled by the edit methods pass def handleShow(self,idd=None): if idd is None: idd = self.monomerListWidget.currentSelection() from core import CCP4Modules,CCP4Utils #print 'CDictDataView.handleShow',idd,self.model,self.model.parent() if self.showWidget is not None: print 'alive?',CCP4Utils.isAlive(self.showWidget) if self.showWidget is None or (not CCP4Utils.isAlive(self.showWidget)): self.showWidget = CCP4Modules.WEBBROWSER().openFile(self.model.parent().__str__()) else: self.showWidget.browserWindow().show() self.showWidget.browserWindow().raise_() if idd is not None: self.showWidget.findText(subString='data_comp_'+idd) def handleMerge(self): from qtgui import CCP4FileBrowser from core import CCP4Modules self.mergeFileBrowser = CCP4FileBrowser.CFileDialog(parent=self,title='Select geometry file to merge into project geometry file', defaultSuffix=CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo(name='application/refmac-dictionary',info='fileExtensions'), filters = ['Geometry file for refinement(*.cif)'], fileMode=QtGui.QFileDialog.ExistingFiles,saveButtonText='Merge these files') self.mergeFileBrowser.show() self.connect(self.mergeFileBrowser,QtCore.SIGNAL('selectFiles'),self.mergeFiles) def mergeFiles(self,selectedFiles): if hasattr(self,'mergeFileBrowser'): self.mergeFileBrowser.close() self.mergeFileBrowser.deleteLater() #print 'CDictDataFileView.mergeFiles',selectedFiles for fileName in selectedFiles: err = self.model.mergeFile(fileName=fileName,overwrite=True) #print 'CDictDataFileView.mergeFiles',err.report(),err.maxSeverity() if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage(windowTitle='Error merging geometry files',parent=self,message='Error attempting to merge geometry files') return def handleDelete(self): idd = self.monomerListWidget.currentSelection() #print 'CDictDataView.handleDelete',idd if idd is None: return err = self.model.delete(idd) if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage(windowTitle='Error deleting item in geometry file',parent=self,message='Error attempting to edit geometry file') return class CDictDataList(QtGui.QTreeWidget): def __init__(self,parent): QtGui.QTreeWidget.__init__(self,parent) self.setMinimumWidth(400) self.setColumnCount(3) self.setHeaderLabels(['Id','Code','Name']) self.setColumnWidth(0,80) self.setColumnWidth(1,80) self.setColumnWidth(2,200) self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) def load(self,monomerList): # monomerList is a Clist of CChemComp #print 'CDictDataList.load',monomerList self.clear() for monomer in monomerList: #print 'CDictDataList.load',monomer.get('id') qList = QtCore.QStringList() for item in ['id','three_letter_code','name']: qList.append(str(monomer.get(item))) item = QtGui.QTreeWidgetItem(qList) treeId=self.addTopLevelItem(item) def handleMonomerDeleted(self,id): #print 'CDictDataList.handleMonomerDeleted',id if self.model().rowCount()==0: return modInxList = self.model().match(id) if len(modInxList)==0: #print 'CDictDataList.handleMonomerDeleted no match to id',id pass else: self.model().removeRow(modInxList[0].row()) def handleMonomerAdded(self,id): pass def currentSelection(self): indices = self.selectionModel().selectedRows() if len(indices) == 0: return None idd = str(indices[0].data().toString()) return idd class CSeqEdit(CCP4Widgets.CTextEdit): def __init__(self,parent): CCP4Widgets.CTextEdit.__init__(self,parent) self.setStyleSheet("CSeqEdit { font-family: Courier ;}") def setValue(self,text): self.blockSignals(True) if text is None: CCP4Widgets.CTextEdit.setValue(self,'') return import re text = re.sub(' ','',text) text = re.sub('\n','',text) nGap = 10 nLine = 60 seqList = [] while len(text)>0: line = text[0:nLine] text = text[nLine:] while len(line)>0: seqList.append(line[0:nGap]+' ') line = line[nGap:] seqList[-1] = seqList[-1]+'\n' newText = '' newText = newText.join(seqList) CCP4Widgets.CTextEdit.setValue(self,newText) self.blockSignals(False) class CAsuContentSeqView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4ModelData.CAsuContentSeq ERROR_CODES = { 101 : { 'description' : 'There is no valid sequence name' }, 102 : { 'description' : 'There is no valid sequence' } } def __init__(self,parent=None,model=None,qualifiers={}): import functools from core import CCP4Modules qualis = {} qualis.update(qualifiers) qualis['vboxLayout'] = True CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis) w = self.layout().takeAt(0).widget() self.widgets['nCopies'] = CCP4Widgets.CIntView(self,qualifiers={ 'editable' : True, 'enumerators':[0,1,2,3,4,5,6,7,8,9,10,11,12] , ' onlyEnumerators' : False } ) self.widgets['nCopies'].setToolTip("The expected number of copies of the sequence") #print 'CAsuContentSeqView nCopies insertPolicy',self.widgets['nCopies'].widget.insertPolicy() self.widgets['name'] = CCP4Widgets.CLineEdit(self) self.widgets['name'].setToolTip("A name for the sequence") self.connect(self.widgets['name'],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.widgets['description'] = CCP4Widgets.CLineEdit(self) self.widgets['description'].setToolTip("A name for the sequence") self.connect(self.widgets['description'],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.widgets['sequence'] = CSeqEdit(self) self.widgets['sequence'].setToolTip("Paste sequence into this window") self.connect(self.widgets['sequence'],QtCore.SIGNAL('textChanged()'),self.updateModelFromView) self.widgets['polymerType'] = CCP4Widgets.CComboBox(self,qualifiers= CCP4ModelData.CAsuContentSeq.CONTENTS['polymerType']['qualifiers'] ) self.widgets['polymerType'].setToolTip("Polymer type - protein/nucleic acid") self.connect(self.widgets['polymerType'],QtCore.SIGNAL('currentIndexChanged(int)'),self.updateModelFromView) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Load sequence from:',self)) whichCombo = CCP4Widgets.CComboBox(self,onlyEnumerators=True) whichCombo.addItems(['Sequence file','Coordinate file','Already loaded sequence']) line.addWidget(whichCombo) line.addStretch(2) self.layout().addLayout(line) seqFileWidget = QtGui.QWidget() seqFileLayout = QtGui.QHBoxLayout() seqFileWidget.setLayout(seqFileLayout) seqFileButton = QtGui.QPushButton('Browse for sequence file',self) self.connect(seqFileButton,QtCore.SIGNAL('clicked()'),functools.partial(self.loadFile,'sequence')) seqFileLayout.addWidget(seqFileButton) seqFileLayout.addStretch(2) self.layout().addWidget(seqFileWidget) coordFileWidget = QtGui.QWidget() coordFileLayout = QtGui.QHBoxLayout() coordFileWidget.setLayout(coordFileLayout) coordFileButton = QtGui.QPushButton('Browse for coordinate file',self) self.connect(coordFileButton,QtCore.SIGNAL('clicked()'),functools.partial(self.loadFile,'coords')) coordFileLayout.addWidget(coordFileButton) coordFileLayout.addStretch(2) self.layout().addWidget(coordFileWidget) coordFileWidget.hide() # Allow using an already imported sequence file seqObjWidget = QtGui.QWidget() seqObjLayout = QtGui.QHBoxLayout() seqObjWidget.setLayout(seqObjLayout) self.seqObjCombo = CCP4Widgets.CComboBox(self,onlyEnumerators=True) self.seqObjCombo.setEditable(False) self.seqObjCombo.addItem('Select sequence..',QtCore.QVariant(-1)) seqObjInfo =CCP4Modules.PROJECTSMANAGER().db().getJobsWithOutputFiles( projectId=self.taskProjectId(),fileType = 'application/CCP4-seq') #print 'CAsuContentSeqView',seqObjInfo for item in seqObjInfo: if item['annotation'] is not None: title = item['annotation'] else: title = 'Sequence from '+CCP4Modules.TASKMANAGER().getTitle(item['taskname']) self.seqObjCombo.addItem(str(item['jobnumber'])+' '+title,QtCore.QVariant(item['fileid'])) self.seqObjCombo.currentIndexChanged.connect(functools.partial(self.loadFile,'seqObj')) seqObjLayout.addWidget(self.seqObjCombo) seqObjLayout.addStretch(2) self.layout().addWidget(seqObjWidget) seqObjWidget.hide() def changeSourceType(sourceType): self.model.unSet() if sourceType == "Sequence file": seqFileWidget.show() coordFileWidget.hide() seqObjWidget.hide() elif sourceType == "Coordinate file": seqFileWidget.hide() coordFileWidget.show() seqObjWidget.hide() else: self.seqObjCombo.setCurrentIndex(0) seqFileWidget.hide() coordFileWidget.hide() seqObjWidget.show() whichCombo.currentIndexChanged[str].connect(changeSourceType) line = QtGui.QHBoxLayout() line.addWidget(w) line.addWidget(self.widgets['nCopies']) line.addWidget(QtGui.QLabel('copies in asymmetric unit of chain named',self)) line.addWidget(self.widgets['name']) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Description:',self)) line.addWidget(self.widgets['description']) self.layout().addLayout(line) self.layout().addWidget(self.widgets['sequence']) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Polymer type:',self)) line.addWidget(self.widgets['polymerType']) self.layout().addLayout(line) self.setModel(model) self.pdbFile = None self.seqFile = None def updataViewFromModel(self): print 'CAsuContentSeqView.updataViewFromModel',self.model CCP4Widgets.CComplexLineWidget.updataViewFromModel(self) def validate(self,isValid=None,excludeWidgets=[],report=None,reportMessage=True): #print 'CAsuContentSeqView.validate' #import traceback #traceback.print_stack(limit=5) if self.model is None: return err = CErrorReport() if len(self.widgets['name'].getValue())==0: self.widgets['name'].validate(isValid=False) isValid = False err.append(self.__class__,101,stack=False) rv = self.model.sequence.validity0(self.widgets['sequence'].getValue()) if rv.maxSeverity()>SEVERITY_WARNING: self.widgets['sequence'].validate(isValid=False,report=rv) isValid = False err.extend(rv) else: self.widgets['sequence'].validate(isValid=True) if err.maxSeverity()>SEVERITY_WARNING: if report is None: report = err.report(ifStack=False,user=True,mode=2) else: report += '\n'+ err.report(ifStack=False,user=True,mode=2) CCP4Widgets.CComplexLineWidget.validate(self,isValid=isValid,excludeWidgets=excludeWidgets,report=report,reportMessage=reportMessage) def setModel(self,model=None): CCP4Widgets.CComplexLineWidget.setModel(self,model=model) ''' if model is None: self.widgets['nCopies'].setModel(None) else: self.widgets['nCopies'].setModel(model.nCopies) ''' def loadFile(self,mode): from qtgui import CCP4FileBrowser from core import CCP4Modules if mode == 'seqObj': fileId = self.seqObjCombo.itemData(self.seqObjCombo.currentIndex()).toString().__str__() if fileId == '-1': return self.doLoadSequenceFile(fileId=fileId) return elif mode == 'sequence': filters = CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo('application/CCP4-seq','filter') self.seqFile = CCP4ModelData.CSeqDataFile(parent=self) self.loadDialog = CCP4FileBrowser.CFileDialog(parent=self, title='Load sequence from file', filters = [ filters ]) self.connect(self.loadDialog,QtCore.SIGNAL('selectFile'),self.loadSequenceFile) elif mode == 'coords': filters = CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo('chemical/x-pdb','filter') self.pdbFile = CCP4ModelData.CPdbDataFile(parent=self) self.loadDialog = CCP4FileBrowser.CFileDialog(parent=self, title='Load sequence from coordinate file', filters = [ filters ]) self.connect(self.loadDialog,QtCore.SIGNAL('selectFile'),self.loadPdbFile) #print 'CAsuContentSeqView.loadFile',repr(self.parent()),repr(self.parent().parent()) if self.window().objectName()=='createAsu': self.window().setModal(False) if sys.platform == "darwin" and not CCP4Modules.PREFERENCES().NATIVEFILEBROWSER: self.loadDialog.exec_() else: self.loadDialog.show() self.loadDialog.raise_() def loadSequenceFile(self,fileName=None): self.pdbFile= None if fileName is not None and len(str(fileName))>0: self.seqFile.setFullPath(fileName) #print 'CAsuContentSeqView.loadSequenceFile',fileName,self.seqFile.format,self.seqFile.identifiers,self.seqFile.__dict__.get('validatedFile','No validated file') #print 'CAsuContentSeqView.loadSequenceFile loadWarning',self.seqFile.fileContent.__dict__.get('loadWarning',None) # Beware if it is a bad PIR file that was reformatted and written to temporary file # set self.seqFile to the temporary file if self.seqFile.fileContent.__dict__.get('loadWarning',None) is not None and \ len(self.seqFile.fileContent.__dict__['loadWarning'])>0 and \ self.seqFile.fileContent.__dict__['loadWarning'][0]['code'] == 408: #print 'resetting seqFile details',self.seqFile.fileContent.__dict__['loadWarning'][0] self.seqFile.setFullPath(self.seqFile.fileContent.__dict__['loadWarning'][0]['details'].split()[-1]) self.seqFile.loadFile() if self.seqFile.format == 'unknown': QtGui.QMessageBox.warning(self,'Reading sequence file','The format of the file has not been recognised. Please edit the sequence to remove any non-sequence information.' ) self.seqFile.unSet() return elif self.seqFile.fileContent.__dict__.get('loadWarning',None) is not None and \ self.seqFile.fileContent.__dict__['loadWarning'].maxSeverity()>SEVERITY_WARNING: self.seqFile.fileContent.__dict__['loadWarning'].warningMessage(windowTitle='Importing sequence file', parent=self,message='Failed importing sequence file') self.seqFile.unSet() return elif len(self.seqFile.identifiers)>1: if getattr(self,'idChooser',None) is None: self.idChooser = QtGui.QListWidget(self) win = self.makeImportReport('Importing '+str(fileName),self.handleIdChooser) win.layout().insertWidget(0,self.idChooser) if isinstance(self.model.parent(),CCP4ModelData.CAsuContentSeqList): self.idChooser.setSelectionMode(QtGui.QListWidget.MultiSelection) title = 'Select one or more sequences from the file' else: self.idChooser.setSelectionMode(QtGui.QListWidget.SingleSelection) title = 'Select one of the sequences in the file' win.layout().insertWidget(0,QtGui.QLabel(title,self)) else: self.idChooser.window().setWindowTitle('Importing '+str(fileName)) self.idChooser.clear() row = 0 for item in self.seqFile.__dict__['identifiers']: self.idChooser.addItem(item) self.idChooser.item(row).setSelected(True) row += 1 self.idChooser.window().show() self.idChooser.window().raise_() return else: self.doLoadSequenceFile(record=0) def doLoadSequenceFile(self,record=0,fileId=None): #print 'doLoadSequenceFile',fileId self.model.unSet() fileAnnotation = None if fileId is not None: from core import CCP4Modules fileName = CCP4Modules.PROJECTSMANAGER().db().getFullPath(fileId=fileId) fileAnnotation = CCP4Modules.PROJECTSMANAGER().db().getFileInfo(fileId=fileId,mode='annotation') #print 'doLoadSequenceFile fileAnnotation',fileAnnotation self.seqFile = CCP4ModelData.CSeqDataFile(parent=self) self.seqFile.setFullPath(fileName) self.seqFile.__dict__['format'] = 'internal' self.seqFile.fileContent.loadInternalFile(str(self.seqFile)) #importInfo = CCP4Modules.PROJECTSMANAGER().db().getImportFileInfo(fileId=fileId) else: #print 'doLoadSequenceFile to loadExternalFile',self.seqFile self.seqFile.fileContent.loadExternalFile(str(self.seqFile),self.seqFile.__dict__['format'],record=record) self.model.source = self.seqFile.__str__() cleanSeq,err = self.model.cleanupSequence(self.seqFile.fileContent.sequence) if err.maxSeverity()>SEVERITY_WARNING: if self.warningMessage(str(self.seqFile)) == QtGui.QMessageBox.Cancel: return if len(cleanSeq) < 1: return self.model.sequence.set(cleanSeq) if hasattr(self.model,"autoSetPolymerType"): self.model.autoSetPolymerType() try: self.model.name.set(self.model.name.fix(self.seqFile.fileContent.name)) except: pass if not self.model.name.isSet(): if fileAnnotation is not None: self.model.name.set(self.model.name.fix(fileAnnotation)) else: import os self.model.name.set(os.path.split(os.path.splitext(self.seqFile.__str__())[0])[1]) self.model.description.set(self.seqFile.fileContent.description) self.updateViewFromModel() self.validate() def warningMessage(self,filename=''): return QtGui.QMessageBox.question(self,'Invalid input '+filename,'The input sequence contains invalid characters (BJOXZ)\n - perhaps it included comments?\nThe invalid characters will be removed. Continue import?',QtGui.QMessageBox.Cancel|QtGui.QMessageBox.Yes) def makeImportReport(self,title,callBack=None): win = QtGui.QDialog(self) win.setWindowTitle(title) win.setLayout(QtGui.QVBoxLayout()) line = QtGui.QHBoxLayout() box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) win.layout().addLayout(line) line.addStretch(1) line.addWidget(box) line.addStretch(1) if callBack is not None: self.connect(box,QtCore.SIGNAL('clicked ( QAbstractButton * )'),callBack) return win def handleIdChooser(self,but): #print 'CAsuContentSeqView.handleIdChooser', but.text().__str__() if self.idChooser.selectionMode() == QtGui.QListWidget.SingleSelection: recordList = [self.idChooser.currentRow()] else: recordList = [] for item in self.idChooser.selectedItems(): recordList.append(self.idChooser.row(item)) win = but.window() win.setModal(False) win.close() #print 'CAsuContentSeqView.handleIdChooser',recordList if but.text().__str__() == 'Cancel': return else: if self.seqFile is not None: self.doLoadSequenceFile(record=recordList[0]) if len(recordList)>1: rv = self.model.parent().extendSeqList(self.seqFile,recordList[1:]) self.parent().parent().updateViewFromModel() self.parent().parent().setListVisible(True) if rv.maxSeverity()>SEVERITY_WARNING: rv.warningMessage(parent=self,windowTitle='Loading '+str(self.seqFile),message='Not all chains loaded') elif self.pdbFile is not None: self.doLoadPdbFile(record=recordList[0]) if len(recordList)>1: rv = self.model.parent().extendSeqList(self.pdbFile,recordList[1:]) self.parent().parent().updateViewFromModel() self.parent().parent().setListVisible(True) if rv.maxSeverity()>SEVERITY_WARNING: rv.warningMessage(parent=self,windowTitle='Loading '+str(self.pdbFile),message='Not all chains loaded') def loadPdbFile(self,fileName): self.seqFile = None self.pdbFile.setFullPath(fileName) if len(self.pdbFile.fileContent.sequences)==0: QtGui.QMessageBox.warning(self,'Reading sequence from coordinate file','No sequences were read from the file\n'+ fileName) return elif len(self.pdbFile.fileContent.sequences)==1: self.doLoadPdbFile(record=0) else: if getattr(self,'idChooser',None) is None: self.idChooser = QtGui.QListWidget(self) win = self.makeImportReport('Importing '+str(fileName),self.handleIdChooser) win.layout().insertWidget(0,self.idChooser) if isinstance(self.model.parent(),CCP4ModelData.CAsuContentSeqList): self.idChooser.setSelectionMode(QtGui.QListWidget.MultiSelection) title = 'Select one or more sequences from the file' else: self.idChooser.setSelectionMode(QtGui.QListWidget.SingleSelection) title = 'Select one of the sequences in the file' win.layout().insertWidget(0,QtGui.QLabel(title,self)) self.idChooser.window().setWindowTitle('Importing '+str(fileName)) self.idChooser.clear() row = 0 for item in self.pdbFile.fileContent.sequences.keys(): self.idChooser.addItem(item) self.idChooser.item(row).setSelected(True) row += 1 if sys.platform == "darwin": self.idChooser.window().exec_() self.idChooser = None else: self.idChooser.window().show() self.idChooser.window().raise_() return def doLoadPdbFile(self,record=0): self.model.unSet() chainId = self.pdbFile.fileContent.sequences.keys()[record] #print 'doLoadPdbFile chainId',chainId cleanSeq,err = self.model.cleanupSequence(self.pdbFile.fileContent.sequences[chainId]) if err.maxSeverity()>SEVERITY_WARNING: if self.warningMessage() == QtGui.QMessageBox.Cancel: return self.model.sequence.set(cleanSeq) if hasattr(self.model,"autoSetPolymerType"): self.model.autoSetPolymerType() self.model.name.set(chainId) self.model.description.unSet() self.updateViewFromModel() self.validate() class CAsuContentSeqListView(CCP4Widgets.CListView): MODEL_CLASS = CCP4ModelData.CAsuContentSeqList def __init__(self,parent=None,model=None,qualifiers={}): #SJM - 'seqeunce' (sic) here is presumably a typo, but I am reluctant to change it without knowing the consequences. qualis = { 'mode' : 'table', 'tableItems' : ['name','nCopies','description','seqeunce','polymerType'] , 'columnHeaders':['Name','Copies','Description','Sequence','Type'], } qualis.update(qualifiers) CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis) self.listWidget.horizontalHeader().setStretchLastSection(True) self.listWidget.setMinimumHeight(150) self.listWidget.setColumnWidth(3,75) def populateListWidget(self,resize=False): CCP4Widgets.CListView.populateListWidget(self,resize=False) self.listWidget.horizontalHeader().setResizeMode(0,QtGui.QHeaderView.ResizeToContents) self.listWidget.horizontalHeader().setResizeMode(1,QtGui.QHeaderView.ResizeToContents) self.listWidget.horizontalHeader().setResizeMode(2,QtGui.QHeaderView.ResizeToContents) self.listWidget.horizontalHeader().setResizeMode(3,QtGui.QHeaderView.Stretch) self.listWidget.horizontalHeader().setResizeMode(4,QtGui.QHeaderView.ResizeToContents) length = 10 newlength = 88 for i in range(self.listWidget.rowCount()): idx = self.listWidget.model().index(i,3) a = str(self.listWidget.model().data(idx).toString()).replace(" ","") b = ' '.join(a[i:i+length] for i in xrange(0,len(a),length)) c = '\n'.join(b[i:i+newlength] for i in xrange(0,len(b),newlength)) self.listWidget.model().setData(idx,"
"+c+"
",QtCore.Qt.ToolTipRole) class CAsuDataFileView(CCP4Widgets.CDataFileView): MODEL_CLASS = CCP4ModelData.CAsuDataFile NOTICE = \ """CCP4i2 now uses a 'Crystal content' file that contains all chain sequences in a crystal instead of separate sequence files. Where necessary you will be able to select chain(s) from the file. In future projects please use the 'Define crystal contents' (in the 'Import ..' module). Please enter all known chain sequences for your structure.""" def __init__(self,parent=None,model=None,qualifiers={}): import functools self.selectionMode = qualifiers.get('selectionMode',0) qualis = { 'vboxLayout' : True } qualis.update(qualifiers) self.selTextList = [] super(CAsuDataFileView,self).__init__(parent=parent,model=None,qualifiers=qualis) if self.editable: #FIXME - Need to provide an API to hide this. self.defineWidget = QtGui.QWidget() defineLayout = QtGui.QHBoxLayout() self.defineWidget.setLayout(defineLayout) defineLayout.setContentsMargins(0,0,0,0) self.layout().addWidget(self.defineWidget) defineButton = QtGui.QPushButton("Specify crystal contents") defineButton.setToolTip("Open the dialog to specify crystal contents information") defineLayout.addWidget(defineButton) defineLayout.addStretch(2) defineButton.clicked.connect(functools.partial(self.handleBrowserOpenFile,"","")) if self.selectionMode>0: if self.selectionMode == 1: self.selectionLabel = QtGui.QLabel('Select one sequence',self) else: self.selectionLabel = QtGui.QLabel('Select one or more sequences',self) self.selectionLabel.setObjectName('italic') self.layout().addWidget(self.selectionLabel) self.selectionFrame = QtGui.QFrame(self) self.selectionFrame.setLayout(QtGui.QGridLayout()) self.layout().addWidget(self.selectionFrame) self.selectionGroup = QtGui.QButtonGroup(self) if self.selectionMode==1: self.selectionGroup.setExclusive(True) else: self.selectionGroup.setExclusive(False) self.selectionButtons = [] self.connect(self.selectionGroup,QtCore.SIGNAL('buttonClicked(int)'),self.updateModelSelection) if self.selectionMode == 2: self.selectionFrame.hide() self.selectionLabel.hide() self.setModel(model) def hideDefineWidget(self): if hasattr(self,"defineWidget"): self.defineWidget.hide() def setModel(self,model): if self.model is not None: self.disconnect(self.model,QtCore.SIGNAL('dataChanged'),self.drawSelection) CCP4Widgets.CDataFileView.setModel(self,model) if self.model is not None: self.connect(self.model,QtCore.SIGNAL('dataChanged'),self.drawSelection) self.connect(self.model.selection,QtCore.SIGNAL('dataChanged'),self.drawSelection) def filterText(self): # make the filters text for QFileDialog textList = CCP4Widgets.CDataFileView.filterText(self) extList = CCP4ModelData.CSeqDataFile().qualifiers('fileExtensions') line = 'Sequence files (*.'+extList[0] for ext in extList: line = line+' *.'+ext line +=')' textList.insert(0,line) return textList def dropTypes(self): # Enable drag-n-drop of old sequence data return ['AsuDataFile','SeqDataFile'] def acceptDropData(self,textData): #print 'CAsuDataFileView.acceptDropData',textData if textData.count('application/CCP4-seq'): self.handleBrowserOpenFile(textData.split(' ')[0]) else: import os from lxml import etree tree = etree.fromstring(textData) if tree.tag == 'SeqDataFile': fileName = os.path.join(tree.xpath('./relPath')[0].text,tree.xpath('./baseName')[0].text) self.handleBrowserOpenFile(fileName,None) else: CCP4Widgets.CDataFileView.acceptDropData(self,textData) def handleBrowserOpenFile(self,fileName,downloadInfo,autoInfoOnFileImport=True,validate=True,updateView=True): import os if fileName.endswith('asu.xml'): CCP4Widgets.CDataFileView.handleBrowserOpenFile(self,fileName,downloadInfo,autoInfoOnFileImport=autoInfoOnFileImport, validate=validate,updateView=updateView) else: #projectId = self.parentTaskWidget().projectId() self.importDialog = QtGui.QDialog(self) self.importDialog.setObjectName('createAsu') self.importDialog.setLayout(QtGui.QGridLayout()) self.importDialog.setWindowTitle('Define crystal content - import sequences:'+os.path.split(fileName)[1]) self.importDialog.setModal(True) self.importDialog.layout().addWidget(QtGui.QLabel(self.NOTICE,self.importDialog)) self.importAsuContent = CCP4ModelData.CAsuContentSeqList(parent=self) self.importAsuContentView = CAsuContentSeqListView(parent=self.importDialog,model=self.importAsuContent) self.importAsuContentView.setListVisible(True) self.importDialog.layout().addWidget(self.importAsuContentView,1,0) self.importDialog.show() self.importAsuContentView.editor.seqFile = CCP4ModelData.CSeqDataFile(parent=self.importAsuContentView) self.importAsuContentView.editor.loadSequenceFile(fileName) butBox = QtGui.QDialogButtonBox(self) self.importDialog.layout().addWidget(butBox,2,0) but = butBox.addButton(QtGui.QDialogButtonBox.Save) self.connect(but,QtCore.SIGNAL('clicked()'),self.doImport) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('clicked()'),self.importDialog.hide) but = butBox.addButton(QtGui.QDialogButtonBox.Help) self.connect(but,QtCore.SIGNAL('clicked()'),self.showHelp) def doImport(self): #print 'CAsuDataFileView.doImport validate',self.importAsuContent,self.importAsuContent.validity(self.importAsuContent.get()) #print 'CAsuDataFileView.doImport jobCombo',self.jobCombo.count() if self.importAsuContent.validity(self.importAsuContent.get())<=SEVERITY_WARNING: self.importDialog.hide() from dbapi import CCP4DbUtils openJob = CCP4DbUtils.COpenJob(projectId=self.parentTaskWidget().projectId()) openJob.createJob(taskName='ProvideAsuContents') openJob.container.inputData.ASU_CONTENT.set(self.importAsuContent) # Do a cleanup of the sequences before saving for seqObj in openJob.container.inputData.ASU_CONTENT: clean,err = seqObj.cleanupSequence(str(seqObj.sequence)) seqObj.sequence.set(clean) if hasattr(seqObj,"autoSetPolymerType"): seqObj.autoSetPolymerType() err = openJob.runJob() if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage(windowTitle='Importing sequence file to Crystal contents',parent=self,message='Failed creating Crystal content file') return self.resetJobCombo = openJob.jobNumber def handleJobFinished(self,args): # After the CDataFileView.handleJobFinished() has updated the jobCombo # make sure the new asu file is selected in it CCP4Widgets.CDataFileView.handleJobFinished(self,args) #print 'CAsuDataFileView.handleJobFinished',getattr(self,'resetJobCombo',None) if getattr(self,'resetJobCombo',None) is not None: idx = self.jobCombo.findText(self.resetJobCombo,QtCore.Qt.MatchStartsWith) #print 'CAsuDataFileView Attempting to reset resetJobCombo',idx if idx>=0: self.jobCombo.setCurrentIndex(idx) del self.resetJobCombo def getMenuDef(self): menu = ['clear','view','exportSeq','editLabel','sep','copy','paste','help'] # For now try selection always open if self.selectionMode>0: menu.insert(menu.index('sep'),'select') return menu def getActionDef(self,name): if name == 'select': def iC(): try: return self.selectionList.isVisible() except: return False return dict ( text = self.tr("Select chains"), tip = self.tr('Select limited set of sequences'), slot = self.showSelection, checkable = True, checked = iC ) elif name == 'exportSeq': return dict ( text = self.tr("Export sequence file"), tip = self.tr('Export sequence file'), slot = self.exportSeq ) elif name == 'editLabel': def e(): return hasattr(self,"jobLabel") return dict ( text = self.tr("Edit label"), tip = self.tr('Edit file label'), slot = self.handleEditLabel, enabled = e, ) return CCP4Widgets.CDataFileView.getActionDef(self,name) def openViewer(self,mode): if not self.model.exists(): return text = '' for seqObj in self.model.fileContent.seqList: text += str(seqObj.nCopies)+' copies of '+str(seqObj.name) +'\n' text += 'Description: '+str(seqObj.description) + '\n' if seqObj.source.isSet(): text += 'Loaded from file: '+str(seqObj.source) + '\n' text += '\n' + seqObj.formattedSequence() + '\n\n' from qtgui import CCP4TextViewer from core import CCP4Modules self.viewDialog = QtGui.QDialog(self.parentTaskWidget()) self.viewDialog.setLayout(QtGui.QVBoxLayout()) self.viewDialog.setWindowTitle(str(self.model.annotation)) self.viewDialog.setMinimumWidth(600) self.viewDialog.setMinimumHeight(300) viewer = CCP4TextViewer.CTextViewer(parent=self.viewDialog) viewer.loadText(text,fixedWidthFont=True) self.viewDialog.layout().addWidget(viewer) self.viewDialog.show() self.viewDialog.raise_() def showSelection(self,visible=None): if visible is None: visible = (not self.selectionFrame.isVisible()) self.selectionFrame.setVisible(visible) self.selectionLabel.setVisible(visible) def getValue(self): rv = CCP4Widgets.CDataFileView.getValue(self) if self.selectionMode>0: selDict = {} for idx in range(len(self.model.fileContent.seqList)): selDict[str(self.model.fileContent.seqList[idx].name)] = self.selectionGroup.button(idx).isChecked() rv['selection'] = selDict #print 'CAsuDataFileView.getValue',rv['selection'] return rv def setValue(self,value): #print 'CAsuDataFileView.setValue',value['selection'] CCP4Widgets.CDataFileView.setValue(self,value) if self.selectionMode==0: return self.selectionGroup.blockSignals(True) for idx in range(len(self.model.fileContent.seqList)): self.selectionGroup.button(idx).setChecked(value['selection'][str(self.model.fileContent.seqList[idx].name)]) self.selectionGroup.blockSignals(False) def handleJobComboChange(self,indx0=None): CCP4Widgets.CDataFileView.handleJobComboChange(self,indx0=indx0) self.updateSelectionViewFromModel() self.validate() def updateModelFromView(self): CCP4Widgets.CDataFileView.updateModelFromView(self) if self.model is None: return if self.selectionMode==0: return self.updateModelSelection() def updateModelSelection(self): selDict = {} if len(self.selectionButtons)==0: return self.connectUpdateViewFromModel(False) for idx in range(len(self.model.fileContent.seqList)): selDict[str(self.model.fileContent.seqList[idx].name)] = self.selectionGroup.button(idx).isChecked() self.model.selection.set(selDict) self.connectUpdateViewFromModel(True) def updateViewFromModel(self): #print 'CAsuFileView.updateViewFromModel' CCP4Widgets.CDataFileView.updateViewFromModel(self) self.updateSelectionViewFromModel() def updateSelectionViewFromModel(self): self.drawSelection() if self.model is None: return #print 'CAsuFileView.updateViewFromModel nResidues',self.model.numberOfResidues(),self.model.molecularWeight() if self.selectionMode == 1 and len(self.model.fileContent.seqList) > 1: self.showSelection(True) elif self.selectionMode == 2: for key,value in self.model.selection.items(): #print 'CAsuFileView.updateViewFromModel selection',key,value if not value: self.showSelection(True) break def drawSelection(self): if self.selectionMode==0: return # Is the selection text up-to-date? selTextList = [] if self.model is not None and self.model.isSet(): for asuObject in self.model.fileContent.seqList: selTextList.append(str(asuObject.name) + ' ' + str(asuObject.description)) #print 'drawSelection',selTextList,selTextList == self.selTextList if selTextList == self.selTextList: return self.selectionGroup.blockSignals(True) # Delete current selection buttons for but in self.selectionButtons: self.selectionGroup.removeButton(but) but.deleteLater() self.selectionButtons = [] # Update the selTextList and redraw the buttons self.selTextList = selTextList idx = 0 for text in self.selTextList: try: if self.selectionMode==1: self.selectionButtons.append(QtGui.QRadioButton(text,self)) else: self.selectionButtons.append(QtGui.QCheckBox(text,self)) self.selectionGroup.addButton(self.selectionButtons[-1],idx) self.selectionFrame.layout().addWidget(self.selectionButtons[-1],idx,0) self.selectionGroup.button(idx).setChecked(self.model.selection[str(self.model.fileContent.seqList[idx].name)]) idx += 1 except Exception as e: print 'Error drawing asu content selection',e self.selectionGroup.blockSignals(False) def showHelp(self): from core import CCP4Modules CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='model_data') def exportSeq(self): from qtgui import CCP4FileBrowser filters = ["FASTA format (*.fasta *.fa *.ffn *.ffa *.fna *.frn)","EMBL format (*.embl)"] self.exportBrowser = CCP4FileBrowser.CFileDialog(self, title='Save selected sequences to file', filters= filters, fileMode=QtGui.QFileDialog.AnyFile ) self.connect(self.exportBrowser,QtCore.SIGNAL('selectFile'),self.doExportSeq) self.exportBrowser.show() def doExportSeq(self,fileName): from core import CCP4Modules theFilter = "fasta" if "." in fileName: theFilter = fileName[fileName.rfind(".")+1:] if theFilter.lower().startswith("em"): theFilter = "embl" else: theFilter = "fasta" elif not CCP4Modules.PREFERENCES().NATIVEFILEBROWSER: if str(self.exportBrowser.widget.fileDialog.selectedFilter()).startswith("EMBL"): theFilter = "embl" fileName += ".embl" else: theFilter = "fasta" fileName += ".fasta" from Bio import SeqIO from Bio.Seq import Seq from Bio.SeqRecord import SeqRecord from Bio.Alphabet import generic_protein from Bio.Alphabet import generic_nucleotide seqs = [] for seqObj in self.model.fileContent.seqList: seqs.append(SeqRecord(Seq(str(seqObj.sequence),generic_protein),name=str(seqObj.description),id=str(seqObj.name),description=str(seqObj.description))) with open(fileName,'w+') as outputFileHandle: SeqIO.write(seqs,outputFileHandle,theFilter)