""" qtgui/CCP4XtalWidgets.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 CCP4XtalWidgets (QtGui) Collection of widgets for crystallographic data types from PyQt4 import QtGui,QtCore import CCP4XtalData import CCP4Widgets from CCP4Utils import safeFloat from CCP4ErrorHandling import * import CCP4Modules class CSpaceGroupsAbstractItemModel(QtCore.QAbstractItemModel): def __init__(self,parent): QtCore.QAbstractItemModel.__init__(self,parent) chiralSpaceGroups= CCP4XtalData.SYMMETRYMANAGER().chiralSpaceGroups crystalSystems = CCP4XtalData.SYMMETRYMANAGER().crystalSystems model = [] for key in crystalSystems: model.append((key,chiralSpaceGroups[key])) self.rootItem = CTreeItem(self,'root',model) def columnCount(self,parent): return 1 def childCount(self): return self.rootItem.childCount() def rowCount(self,index): if not index.isValid(): return self.childCount() else: return index.internalPointer().childCount() def child(self,row): return self.rootItem.child(row) def data(self, index, role): if not index.isValid(): return None item = index.internalPointer() #return item.data(index.column(),role) return item.data(role) def index(self, row, column, parent): if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent): return QtCore.QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) #print 'CProjectModel.index',row, column, parent,childItem.getName() if childItem: return self.createIndex(row, column, childItem) else: return QtCore.QModelIndex() def parent(self,index): if not index.isValid(): return QtCore.QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent if parentItem == self.rootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) class CSpaceGroupTreeView(QtGui.QTreeView): def mousePressEvent(self,event): if event.button() == QtCore.Qt.LeftButton: index = self.indexAt(event.pos()) #print 'CSpaceGroupTreeView.mousePressEvent',index.row(),index.parent().isValid(),self.parent().parent() if index.isValid() and not index.parent().isValid(): #print 'CSpaceGroupTreeView.mousePressEvent expanding' self.setExpanded(index,(not self.isExpanded(index))) # Put temporary block on cling the popup menu self.parent().parent().blockClose = True event.accept() return class CSpaceGroupCombo(QtGui.QComboBox): def __init__(self,parent): QtGui.QComboBox.__init__(self,parent) self.blockClose = False def hidePopup(self): #print 'CSpaceGroupCombo.hidePopup' if self.blockClose: self.blockClose = False return QtGui.QComboBox.hidePopup(self) class CSpaceGroupView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4XtalData.CSpaceGroup def __init__(self,parent=None,model=None,qualifiers={}): qualis = {} qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis) wQualis = { } wQualis.update(qualis) wQualis['charWidth'] = 12 self.layout().addWidget(QtGui.QLabel(qualifiers.get('label','Space group'),self)) if self.editable: self.widget = CCP4Widgets.CSearchBox() self.widget.setHitList( CCP4XtalData.SYMMETRYMANAGER().nonEnantiogenicList()) self.connect(self.widget,QtCore.SIGNAL('changed'),self.updateModelFromView) else: self.widget = QtGui.QLabel(self) self.layout().addWidget(self.widget) self.setModel(model) #print 'CSpaceGroupView.__init__',model self.connect(self.widget,QtCore.SIGNAL('dataSelected'),self.updateModelFromView) class CCellView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4XtalData.CCell ERROR_CODES = { } STRETCH = 5 def __init__(self,parent=None,model=None,qualifiers={}): qualis = {'gridLayout':True} qualis.update(qualifiers) #print 'CCellView.__init__',qualifiers,qualis; import sys; sys.stdout.flush() CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualis) #print 'CCellView.__init__',self.layout() for row,rowItems in [ [ 0, ['a','b','c']],[1,['alpha','beta','gamma']]]: col = 0 for item in rowItems: if item in ['alpha','beta','gamma']: label = QtGui.QLabel('&'+item+';') else: label = QtGui.QLabel(item) label.setTextFormat(QtCore.Qt.RichText) col = col+1 self.layout().addWidget(label,row,col) if self.editable: if model is not None: wQualis = model.get(item).qualifiers() else: wQualis = {} if model is not None: wQualis['toolTip']=model.getDataObjects(item).qualifiers('toolTip') self.widgets[item] = CCP4Widgets.CFloatView(self,qualifiers=wQualis) self.connect(self.widgets[item],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.connect(self.widgets[item],QtCore.SIGNAL('acceptDropData'),self.acceptDropData) #w.setReadOnly(1-self.editable) else: self.widgets[item] = CCP4Widgets.CLabel(self,qualis,uneditable=True) col = col+1 self.layout().addWidget(self.widgets[item],row,col) #self.layout().setStretchFactor(self.widgets[item],2) if self.editable and model is not None: self.setModel(model) def getMenuDef(self): return ['copy','paste','help'] def updateModelFromText(self): self.updateModelFromView() class CSpaceGroupCellView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4XtalData.CSpaceGroupCell def __init__(self,parent=None,model=None,qualifiers={}): qualis = { 'gridLayout' : True, 'iconName' : 'cell' } qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualis) wQualis = { 'charWidth':12 } if qualis.get('editable',True): self.widgets['spaceGroup'] = CCP4Widgets.CSearchBox() self.widgets['spaceGroup'].setHitList( CCP4XtalData.SYMMETRYMANAGER().nonEnantiogenicList()) self.connect(self.widgets['spaceGroup'],QtCore.SIGNAL('changed'),self.updateModelFromView) else: self.widgets['spaceGroup'] = CCP4Widgets.CLabel(self) #self.widgets['spaceGroup'] = CSpaceGroupView(self,qualifiers=wQualis) layout = QtGui.QHBoxLayout() layout.addWidget(QtGui.QLabel('SpaceGroup',self)) layout.addWidget(self.widgets['spaceGroup']) self.layout().addLayout(layout,1,1) if self.editable: self.loadButton = QtGui.QPushButton('Load from another file',self) self.loadButton.setToolTip('Take cell parameters from a different PDB or MTZ file') self.layout().addWidget(self.loadButton,0,1) self.connect(self.loadButton,QtCore.SIGNAL('clicked()'),self.handleLoadButton) self.widgets['cell'] = CCellView(parent=self.parent(),qualifiers=qualis) self.widgets['cell'].iconButton.deleteLater() self.layout().addWidget(self.widgets['cell'],0,2,2,1) #print 'CSpaceGroupCellView widgets',self.widgets self.setModel(model) def handleLoadButton(self): #print 'CSpaceGroupCellView.handleLoadButton' import CCP4Data filterText = [ 'Coordinate file (*.pdb *.cif *.ent)' ,'Experimental data file (*.mtz *.cif *.ent)'] import CCP4FileBrowser self.fileBrowser = CCP4FileBrowser.CFileDialog(self, title='Select experimental data file or coordinate file', filters= filterText, defaultFileName='') self.fileBrowser.setStyleSheet("") self.fileBrowser.setDownloadMode(modeList=['ebiPdb','ebiSFs'],projectId=self.parentTaskWidget().projectId()) self.connect(self.fileBrowser,QtCore.SIGNAL('selectFile'),self.loadFromFile) self.fileBrowser.show() def loadFromFile(self,fileName): #print 'CSpaceGroupCellView.loadFromFile',fileName,type(fileName) import os ext = os.path.splitext(fileName)[1] self.model.blockSignals(True) #print 'CSpaceGroupCellView.loadFromFile',ext if ext == '.pdb': import CCP4ModelData obj = CCP4ModelData.CPdbDataFile(fileName) #print 'CSpaceGroupCellView.loadFromFile',obj.fileContent.mmdbManager.GetCell(),obj.fileContent.mmdbManager.GetSpaceGroup() self.model.unSet() try: self.model.cell.set(obj.fileContent.mmdbManager.GetCell()[1:7] ) except: pass try: self.model.spaceGroup.set(obj.fileContent.mmdbManager.GetSpaceGroup() ) except: pass elif ext == '.mtz': obj = CCP4XtalData.CMtzDataFile(fileName) self.model.unSet() try: self.model.spaceGroup.set(obj.fileContent.spaceGroup) except: pass try: self.model.cell.set(obj.fileContent.cell) except: pass self.model.blockSignals(False) self.updateViewFromModel() self.validate() def dropTypes(self): return ['MtzDataFile','MiniMtzDataFile','ObsDataFile','PhsDataFile','FreeRDataFile','MapCoeffsDataFile','PdbDataFile'] def acceptDropData(self,textData): #print 'CSpaceGroupCellView.acceptDropData',textData from lxml import etree tree = etree.fromstring(textData) import os try: path = os.path.join(tree.xpath('//relPath')[0].text.__str__(),tree.xpath('//baseName')[0].text.__str__()) #print 'CSpaceGroupCellView.acceptDropData path',path except: pass else: self.loadFromFile(path) class CTreeItem: def __init__(self,parent=None,data={},children=[]): #if len(data)>0: # print 'CTreeItem.__init__',self,parent,str(data[QtCore.Qt.DisplayRole].toString()) self.parent = parent self.myData = {} if isinstance(data,dict): self.myData.update(data) else: self.myData[QtCore.Qt.DisplayRole] = data self.children = [] for c in children: if isinstance(c,(list,tuple)): self.appendChild(CTreeItem(self,c[0],c[1])) else: self.appendChild(CTreeItem(self,c)) def childCount(self): return len(self.children) def child(self,row): if row>=0 and row0: return (str(columnGroup.groupType) in programGroupTypes) else: if len(columnGroup.columns) != len(programColumnGroup): return False for i in range(len(columnGroup.columns)): if columnGroup.columns[i].columnType not in programColumnGroup[i].columnType: #print 'mismatch',columnGroup.columns[i].columnType,programColumnGroup[i].columnType return False return True def flags(self,modelIndex): #if not modelIndex.parent().isValid() and not modelIndex.row() == 0: if not modelIndex.parent().isValid(): return QtCore.Qt.ItemIsEnabled else: return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def data(self,modelIndex,role): if not modelIndex.isValid(): return None item = modelIndex.internalPointer() return item.data(role) def headerData(self,section,orientation,role): return QtCore.QVariant() def rowCount(self,modelIndex): if modelIndex.column() > 0: return 0 if not modelIndex.isValid(): parentItem = self.rootItem else: parentItem = modelIndex.internalPointer() return parentItem.childCount() def columnCount(self,modelIndex): return 1 def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QtCore.QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) if childItem is not None: return self.createIndex(row, column, childItem) else: return QtCore.QModelIndex() def parent(self, modelIndex): if not modelIndex.isValid(): return QtCore.QModelIndex() childItem = modelIndex.internalPointer() parentItem = childItem.parent if parentItem is None: return None elif parentItem == self.rootItem: return QtCore.QModelIndex() else: #print 'CProgramColumnGroupModel.parent',parentItem.row() return self.createIndex(parentItem.row(), 0, parentItem) class CProgramColumnGroupQtView(QtGui.QTreeView): def __init__(self,parent=None): QtGui.QTreeView.__init__(self,parent) self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.header().setVisible(False) class CProgramColumnGroupCombo( QtGui.QComboBox ): def __init__(self,parent): QtGui.QComboBox.__init__(self,parent) self.setModel(CProgramColumnGroupModel(self)) self.setView(CProgramColumnGroupQtView(self)) #self.view().setSelectionModel(QtGui.QItemSelectionModel(self.model())) #self.connect(self.view().selectionModel(),QtCore.SIGNAL('selectionChanged(const QItemSelection&,const QItemSelection&)'),self.beep) self.setEditable(False) def beep(self): print 'CProgramColumnGroupCombo.beep',self.currentIndex(),self.view().selectionModel().currentIndex().row() def currentColumnGroupIndices(self): modelIndex = self.view().selectionModel().currentIndex() if modelIndex.isValid(): data = modelIndex.internalPointer().data(QtCore.Qt.UserRole) if data is None: return -1,-1 val = data.toString().__str__().split('.') #print 'currentColumnGroupIndices',val,self.currentIndex() return int(val[0]),int(val[1]) else: return -1,-1 def setCurrentColumnGroup(self,dataset,columnGroupText): #print 'CProgramColumnGroupCombo.setCurrentColumnGroup',dataset,columnGroupText ds = self.model().rootItem.findChild(QtCore.Qt.DisplayRole,dataset) #print 'CProgramColumnGroupCombo.setCurrentColumnGroup ds',ds if ds is None: return cg = ds.findChild(QtCore.Qt.DisplayRole,QtCore.QVariant(columnGroupText)) #print 'CProgramColumnGroupCombo.setCurrentColumnGroup cg',cg if cg is not None: dsi = self.model().index(ds.row(),0, QtCore.QModelIndex()) cgi = self.model().index(cg.row(),0,dsi) #print 'setCurrentColumnGroup',dsi.row(),cgi.row() #self.view().selectionModel().setCurrentIndex(cgi,QtGui.QItemSelectionModel.ClearAndSelect) #self.view().selectionModel().select(cgi,QtGui.QItemSelectionModel.ClearAndSelect) #self.view().selectionModel().emitSelectionChanged(QtGui.QItemSelection(cgi,cgi),QtGui.QItemSelection()) #indx = self.findText(cg.data(QtCore.Qt.DisplayRole).toString().__str__()) #print 'CProgramColumnGroupCombo.setCurrentColumnGroup combo indx',cg.data(QtCore.Qt.DisplayRole).toString(),indx self.setRootModelIndex( QtCore.QModelIndex()) self.setCurrentIndex(cgi.row()) class CProgramColumnGroupView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4XtalData.CProgramColumnGroup MARGIN = 0 STRETCH = 5 ERROR_CODES = { 101 : { 'description' : 'CProgramColumnGroup model not attached to a CMtzDataFile' }, 102 : { 'description' : 'No CProgramColumnGroup model assigned for displaying MTZ column selection' }, 103 : { 'description' : 'Error attempting to update display of MTZ columns' }, 104 : { 'description' : 'Error number of MTZ columns in GUI does not match number in model' }, 105 : { 'description' : 'MTZ file contents not available' } } def __init__(self,parent=None,model=None,qualifiers={},**kw): qualis = {} qualis.update(model.qualifiers()) qualis.update(qualifiers) qualis.update(kw) qualis['gridLayout'] = True CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis) iconWidgetItem = self.layout().takeAt(0) iconWidgetItem.widget().deleteLater() self.widgets = [] self.setModel(model) def setModel(self,model=None): #print 'CProgramColumnGroupView.setModel',model if model == self.model: return if model is None or isinstance(model,self.MODEL_CLASS): if self.model is not None: CCP4Widgets.CViewWidget.unsetModel(self) self.model = model ''' if model is not None: self.connectUpdateViewFromModel(True) self.drawColumns() try: mtzDataObject = self.model.getDataByKey('mtzFileKey') except: mtzDataObject = None #print 'CProgramColumnGroupView.setModel mtzDataObject',mtzDataObject.objectName(),mtzDataObject.fileContent if mtzDataObject is not None: self.connect(mtzDataObject,QtCore.SIGNAL('dataChanged'),self.populateColumns) if mtzDataObject.fileContent is not None: if self.editable: self.populateColumns() #elf.updateViewFromModel() #self.updateFromFirstColumn() else: self.updateViewFromModel() ''' def drawColumns(self,editable=None): if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath()) n = -1 toolTip = self.model.qualifiers('toolTip') if editable is not None: self.editable = editable #print 'CProgramColumnGroupView.drawColumns columnGroupNames', self.model.columnGroupNames() for name in self.model.columnGroupNames(): n = n + 1 label = QtGui.QLabel(name,self) if self.editable: self.widgets.append( CCP4Widgets.CComboBox(self,qualifiers= { 'dragType' : self.dragType() }) ) self.widgets[-1].setMinimumContentsLength(25) self.widgets[-1].setEditable(False) if toolTip is not NotImplemented: self.widgets[-1].setToolTip(toolTip) else: self.widgets.append(CCP4Widgets.CLabel(self , { 'charWidth' : 25, 'editable' :False, 'dragType' : self.dragType() } )) row = n/2 column = (n%2) * 2 self.layout().addWidget(label,row,column+1) self.layout().addWidget(self.widgets[n],row,column+2) if self.editable: self.connect(self.widgets[-1],QtCore.SIGNAL('acceptDropData'),self.acceptDropData) # Just attempt to update all columns in sync with the first column if n == 0: self.connect(self.widgets[-1],QtCore.SIGNAL('currentIndexChanged(int)'),self.updateFromFirstColumn) #self.connect(self.widgets[-1],QtCore.SIGNAL('dataChanged'),self.updateFromFirstColumn) else: self.connect(self.widgets[-1],QtCore.SIGNAL('currentIndexChanged(int)'),self.updateModelFromView) #self.connect(self.widgets[-1],QtCore.SIGNAL('dataChanged'),self.updateModelFromView) if n%2 == 1: pass def populateColumns(self,listOfColumnGroups=[]): for widget in self.widgets: widget.blockSignals(True) widget.clear() if self.model is None: for widget in self.widgets: widget.blockSignals(False) return #for item in listOfColumnGroups: print 'populateColumns listOfColumnGroups',item.get() if len(listOfColumnGroups)==1: #load into a label widget for n in range(0,self.model.nColumns()): label = str(listOfColumnGroups[0].columnList[n].dataset)+'/'+str(listOfColumnGroups[0].columnList[n].columnLabel) self.widgets[n].setText(label) else: for n in range(0,self.model.nColumns()): for item in listOfColumnGroups: label = str(item.columnList[n].dataset)+'/'+str(item.columnList[n].columnLabel) self.widgets[n].addItem(label,QtCore.QVariant(label)) for widget in self.widgets: widget.blockSignals(False) self.updateFromFirstColumn() #def updateFromFirstColumn(self,firstColumnIndex): def updateFromFirstColumn(self,indx=None): #print 'CProgramColumnGroupView.updateFromFirstColumn',indx if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath()) data = {} nameList = self.model.columnGroupNames() #print 'updateFromFirstColumn',self.model,type(self.model),nameList if self.editable: data[nameList[0]] = str(self.widgets[0].currentText()) else: data[nameList[0]] = str(self.widgets[0].text()) for name in nameList[1:]: data[name] = None #self.model.__setattr__(name,columnLabel) #print 'CProgramColumnGroupView.updateFromFirstColumn',self.model.objectName(),data self.model.set(data,fix=True) self.updateViewFromModel() def updateViewFromModel(self): if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath()) #print 'CProgramColumnGroupView.updateViewFromModel',self.model.objectName(),self.model if len(self.widgets)=0: self.widgets[n].setCurrentIndex(indx) else: #print 'CProgramColumnGroupView.updateViewFromModel',n,name,data[name] self.widgets[n].setValue(data[name]) else: if self.editable: self.widgets[n].setCurrentIndex(self.widgets[n].count()-1) else: self.widgets[n].setValue('') self.widgets[n].blockSignals(False) def updateModelFromView(self,**kw): if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath()) if len(self.widgets)SEVERITY_WARNING: err.warningMessage('Importing experimental data','Failed loading cif format file',parent=self) self.model.unSet() return errors = self.model.validColumns() #print 'CMiniMtzDataFileView.handleBrowserOpenFile',errors.report() #print 'CMiniMtzDataFileView.handleBrowserOpenFile sourceFileAnnotation',self.model.__dict__.get('sourceFileAnnotation','') if errors.maxSeverity()>SEVERITY_WARNING: #print errors.report() if errors.count(cls=self.model.__class__,code=206)>0: QtGui.QMessageBox.warning(self,'Error in selected MTZ file','This file contains unmerged data - use Data Reduction task to import it') self.model.unSet() return elif errors.count(cls=self.model.__class__,code=203)>0: QtGui.QMessageBox.warning(self,'Error in selected MTZ file','Selected MTZ file does not contain correct type of data') self.model.unSet() return elif errors.count(cls=self.model.__class__,code=204)>0 or errors.count(cls=self.model.__class__,code=205)>0: #applyNow = (errors.count(cls=self.model.__class__,code=205)>0) #print 'handleBrowserOpenFile applyNow',applyNow,filename try: self.dialog=CSelectColumnsWidget(parent=self,model=self.model,applyNow=False,filename=filename) except CException as e: mess = 'This file '+ filename + '\ndoes not contain the appropriate data: ' for rC in self.model.requiredContent(): mess = mess + self.model.CONTENT_ANNOTATION[rC-1]+',' QtGui.QMessageBox.warning(self.parentTaskWidget(),'Error in selected MTZ file',mess[0:-1]) self.model.unSet() return else: self.connect(self.dialog,QtCore.SIGNAL('apply'),self.handleDialogApply) self.connect(self.dialog,QtCore.SIGNAL('cancel'),self.handleDialogCancel) else: # Selected file has correct complement of columns but needs to be imported to same # target filename as used by self.model.splitMtz() self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber()) if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT and not self.model.dbFileId.isSet(): # If this is a downloaded file then it will have some provenance info in # self.model.sourceFileAnnotation put there by CDataFileView.handleBrowserOpenFile() self.openInfo(label=self.model.qualifiers('guiLabel').lower(),sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation','')) def handleDialogApply(self): selectedColumns = self.dialog.getSelection() #print 'handleDialogApply',selectedColumns try: self.dialog.close() except: pass jobId = self.parentTaskWidget().jobId() projectId = self.parentTaskWidget().projectId() sourceFileName = self.model.__str__() error = self.model.splitMtz(jobId=jobId,projectId=projectId,contentFlag=selectedColumns[0],i2Labels=selectedColumns[1],columnLabels=selectedColumns[3]) #print 'CMiniMtzDataFileView.handleDialogApply',error self.model.emitDataChanged() if error.maxSeverity()==SEVERITY_WARNING and error[0]['code']==212: mess = QtGui.QMessageBox.warning(self,self.windowTitle(),'This data is already imported as\n'+error[0]['details']) self.loadJobCombo() self.updateJobCombo() self.validate() elif error.maxSeverity()>=SEVERITY_WARNING: if error[0]['code']==211: mess = QtGui.QMessageBox.warning(self,self.windowTitle(),'No column data selected') else: error.warningMessage(windowTitle='Splitting MTZ: '+sourceFileName,jobId=jobId,parent=self) self.model.unSet() self.updateJobCombo() self.validate() else: self.loadJobCombo() self.updateJobCombo() self.validate() if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT: #print 'CMiniMtzDaaFile.handleDialogApply sourceFileReference',self.model.__dict__.get('sourceFileReference','') # If this is a downloaded file then it will have some provenance info in # self.model.sourceFileAnnotation put there by CDataFileView.handleBrowserOpenFile() self.openInfo(label=self.model.qualifiers('guiLabel').lower()+' '+self.model.__dict__.get('sourceFileReference',''), sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation','')) self.dialog.deleteLater() def handleDialogCancel(self): self.dialog.close() self.dialog.deleteLater() self.model.unSet() class CMergeMiniMtzView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS =CCP4XtalData.CMergeMiniMtz def __init__(self,parent=None,model=None,qualifiers={},**kw): qualis = { 'vboxLayout' : True, 'iconName' : 'MiniMtzDataFile', 'dragType' : ['MiniMtzDataFile','ObsDataFile','PhsDataFile','FreeRDataFile','MapCoeffsDataFile'], 'iconButton' : True } qualis.update(qualifiers) qualis.update(kw) CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis,**kw) self.setFrameShape(QtGui.QFrame.Box) iconWidgetItem = self.layout().takeAt(0) qualis['iconButton'] = False del qualis['vboxLayout'] self.widgets['fileName'] = CMiniMtzDataFileView(parent=self,qualifiers=qualis) self.widgets['columnTag'] = CCP4Widgets.CStringView(parent=self,qualifiers=qualis) self.widgets['columnTag'].widget.setMaximumWidth(300) self.widgets['columnTag'].widget.setMinimumWidth(300) self.widgets['columnNames'] = CCP4Widgets.CStringView(parent=self,qualifiers=qualis) self.widgets['fileName'].setToolTip('Select an experimental data object') self.widgets['columnNames'].setToolTip('Comma-separated list of output column names - spaces will be ignored') line = QtGui.QHBoxLayout() line.addWidget(iconWidgetItem.widget()) line.addWidget( self.widgets['fileName'] ) self.layout().addLayout( line ) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Tag for column names')) line.addWidget(self.widgets['columnTag'] ) line.addWidget(QtGui.QLabel('(maximum 20 characters)')) line.addStretch(2) self.layout().addLayout( line ) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Output column names')) line.addWidget(self.widgets['columnNames'] ) self.layout().addLayout( line ) self.setModel(model) def openViewer(self,mode): self.widgets['fileName'].openViewer(mode) def setModel(self,model): CCP4Widgets.CComplexLineWidget.setModel(self,model) if model is None: self.widgets['fileName'].setModel(None) self.widgets['columnTag'].setModel(None) self.widgets['columnNames'].setModel(None) else: self.widgets['fileName'].setModel(model.fileName) self.widgets['columnTag'].setModel(model.columnTag) self.widgets['columnNames'].setModel(model.columnNames) # Beware the dataChanged signal from CDataFile may come before dbFileId is set # import functools self.connect(self.model.fileName.dbFileId,QtCore.SIGNAL('dataChanged'),functools.partial(self.updateColumnNames,True)) self.connect(self.widgets['columnTag'].widget,QtCore.SIGNAL('editingFinished()'),functools.partial(self.updateColumnNames,False)) #self.model.setColumnTag(overwrite=False) self.model.setColumnNames('applyTag',overwrite=False) def updateViewFromModel(self): if self.model is None: return for item in ['fileName','columnTag','columnNames']: self.widgets[item].updateViewFromModel() def updateModelFromView(self): if self.model is None: return for item in ['fileName','columnTag','columnNames']: self.widgets[item].updateModelFromView() def updateColumnNames(self,setColumnTag=False): #print 'CMergeMiniMtzView.updateColumnNames',setColumnTag if setColumnTag: self.model.setColumnTag(overwrite=True) self.model.setColumnNames('applyTag',overwrite=True) def getMenuDef(self): if self.editable: #menu = ['clear','view','annotate','sep','copy','paste','help'] menu = ['clear','view_ViewHKL','sep','copy','paste','help'] else: menu = ['view_ViewHKL','sep','copy','help'] if self._stacked: menu.insert(0,'handleStack') return menu def acceptDropData(self,textData): #print 'CMergeMiniMtzView.acceptDropData',textData from lxml import etree tree = etree.fromstring(textData) tree.tag = self.dragType() #print 'CMergeMiniMtzView.acceptDropData',textData,tree.tag self.connectUpdateViewFromModel(False) self.model.fileName.unSet() self.model.fileName.setEtree(tree) #print 'CMergeMiniMtzView.acceptDropData model',self.model.fileName self.connectUpdateViewFromModel(True) self.updateViewFromModel() #print 'CMergeMiniMtzView.acceptDropData validity',self.model.validity(self.model.get()).report() self.validate() class CMergeMiniMtzListView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS =CCP4XtalData.CMergeMiniMtzList def __init__(self,parent=None,model=None,qualifiers={},**kw): ''' qualis = { 'mode' : 'table', 'tableItems' : ['fileName','columnNames'], 'columnHeaders':['Filename','Column names'] } qualis.update(qualifiers) CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis) self.listWidget.setColumnWidth(0,270) self.listWidget.setColumnWidth(1,270) ''' qualis = { 'vboxLayout' : True, 'iconButton' : False } qualis.update(qualifiers) qualis.update(kw) CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis,**kw) self.widgets = [] if self.editable: but = QtGui.QPushButton('Add input experimental data object',self) self.connect(but,QtCore.SIGNAL('released()'),self.handleAddObject) self.layout().addWidget(but) self.setModel(model) def setModel(self,model=None): self.model = model if model is None: return for item in model.__dict__['_value']: #print 'CMergeMiniMtzListView.setModel make widget for item',item self.widgets.append(CMergeMiniMtzView(self,model=item,qualifiers={'editable':self.editable})) self.layout().insertWidget(self.layout().count()-1,self.widgets[-1]) def handleAddObject(self): self.model.addItem() self.widgets.append(CMergeMiniMtzView(self,model=self.model[-1],qualifiers={'editable':self.editable})) self.layout().insertWidget(self.layout().count()-1,self.widgets[-1]) def setValue(self,value=[]): #print 'CMergeMiniMtzListView.setValue',value if len(value)>len(self.widgets): for mod in value[len(self.widgets):]: self.widgets.append(CMergeMiniMtzView(self,model=mod,qualifiers={'editable':self.editable})) self.layout().insertWidget(self.layout().count()-1,self.widgets[-1]) #print 'CMergeMiniMtzListView.setValue',len(self.widgets),mod indx = 0 for item in value: self.widgets[indx].setValue(item) indx += 1 self.validate() def getValue(self,index=-1): #print 'CMergeMiniMtzListView.getValue NOT IMPLEMENTED' value = [] if index<0: first = 0 last = self.body.layout().count() else: first = index last = index + 1 return value def updateViewFromModel(self): for w in self.widgets: w.updateViewFromModel() self.validate() def updateModelFromView(self): for w in self.widgets: w.updateModelFromView() class CRunBatchRangeView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS =CCP4XtalData.CRunBatchRange def __init__(self,parent=None,model=None,qualifiers={},**kw): qualis = {} qualis.update(qualifiers) qualis.update(**kw) #print 'CRunBatchRangeView',qualis CCP4Widgets.CComplexLineWidget. __init__(self,parent=parent,qualifiers=qualis,**kw) # Remove the icon which is not doing much for us icon = self.layout().takeAt(0).widget() icon.deleteLater() for item in ['runNumber','batchRange0','batchRange1','fileNumber']: self.widgets[item] = CCP4Widgets.CIntView(parent=self,qualifiers=qualis) self.connect(self.widgets[item], QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.connect(self.widgets[item], QtCore.SIGNAL('enterKeyPress'),self.updateModelFromView) self.fileNumberLabel = QtGui.QLabel('file number',self) self.layout().addWidget(QtGui.QLabel('Run number',self)) self.layout().addWidget( self.widgets['runNumber'] ) self.layout().addWidget(self.fileNumberLabel) self.layout().addWidget( self.widgets['fileNumber'] ) self.layout().addWidget(QtGui.QLabel('for batch range',self)) self.layout().addWidget(self.widgets['batchRange0']) self.layout().addWidget(QtGui.QLabel('to',self)) self.layout().addWidget(self.widgets['batchRange1']) self.layout().addStretch(1) self.showFileNumber(qualis.get('showFileNumber',False)) if model is not None: self.setModel(model) def showFileNumber(self,mode): self.fileNumberLabel.setVisible(mode) self.widgets['fileNumber'].setVisible(mode) def setModel(self,model): # Reimplement to ensure this is validiated when any data item changes # (why is this not done in the base class?) if self.model is not None: for item in ['runNumber','batchRange0','batchRange1']: self.disconnect(self.model.__getattr__(item),QtCore.SIGNAL('dataChanged'),self.validate) CCP4Widgets.CComplexLineWidget.setModel(self,model) if self.model is not None: for item in ['runNumber','batchRange0','batchRange1']: self.connect(self.model.__getattr__(item),QtCore.SIGNAL('dataChanged'),self.validate) class CRunBatchRangeListView(CCP4Widgets.CListView): MODEL_CLASS =CCP4XtalData.CRunBatchRangeList def __init__(self,parent=None,model=None,qualifiers={},**kw): if qualifiers.get('showFileNumber',False): qualis = { 'mode' : 'table', 'tableItems' : ['runNumber','fileNumber','batchRange0','batchRange1'], 'columnHeaders':['Run number','File number','Batch start','Batch end'] } else: qualis = { 'mode' : 'table', 'tableItems' : ['runNumber','batchRange0','batchRange1'], 'columnHeaders':['Run number','Batch start','Batch end'] } qualis.update(qualifiers) qualis.update(**kw) #print 'CRunBatchRangeListView',qualis CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis,editorQualifiers={ 'showFileNumber' :qualifiers.get('showFileNumber',False) } ) class CReindexOperatorView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4XtalData.CReindexOperator def __init__(self,parent=None,model=None,qualifiers={},**kw): CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualifiers) #self.layout().takeAt(0).widget().deleteLater() self.layout().addWidget(QtGui.QLabel('Reindex operator',self)) for key in ['h','k','l']: self.widgets[key] = CCP4Widgets.CLineEdit(self) self.widgets[key].setToolTip('Enter operator for '+key) self.connect(self.widgets[key], QtCore.SIGNAL('editingFinished()'),self.updateModelFromView) self.connect(self.widgets[key], QtCore.SIGNAL('enterKeyPress'),self.updateModelFromView) self.layout().addWidget(QtGui.QLabel(key+'=',self)) self.layout().addWidget(self.widgets[key]) self.layout().setStretchFactor(self.widgets[key],2) if model is not None: self.setModel(model) def help(self): import CCP4Utils import os page = os.path.join(CCP4Utils.getCCP4Dir(),'html','reindexing.html') if not os.path.exists(page): page = os.path.join(CCP4Utils.getCCP4Dir(),'docs','reindexing.html') #print 'CReindexOperatorView.help',page,os.path.exists(page) if os.path.exists(page): CCP4Modules.WEBBROWSER().loadWebPage(fileName=page) class CColumnGroupListView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS =CCP4XtalData.CColumnGroupList def __init__(self,parent=None,model=None,qualifiers={},**kw): import functools qualis = { 'vboxLayout' : True } qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis) line = QtGui.QHBoxLayout() line.addWidget(self.layout().takeAt(0).widget()) line.addWidget(QtGui.QLabel('Select column groups to be imported as separate data objects',self)) self.layout().addLayout(line) self.grid = QtGui.QGridLayout() self.layout().addLayout(self.grid) col=0 for item in ['Dataset','Type','Column labels and types']: l = QtGui.QLabel(item,self) l.setObjectName('bold') col = col + 1 self.grid.addWidget(l,0,col) line = QtGui.QHBoxLayout() #for item in ['Select all','Deselect all','Unique observed data']: for item in ['Select all importable','Deselect all']: button = QtGui.QPushButton(item,self) line.addWidget(button) self.connect(button,QtCore.SIGNAL('clicked()'),functools.partial(self.handleButton,item)) line.addStretch(1) self.layout().addLayout(line) if model is not None: self.setModel(model) def handleButton(self,mode): #print 'CColumnGroupListView.handleButton',mode if mode == 'Unique observed data': pass else: flag = (mode == 'Select all importable') for row in range(1,self.grid.rowCount()): w = self.grid.itemAtPosition(row,0).widget() if not isinstance(w,CCP4Widgets.CCheckBoxUneditable): w.setChecked(flag) def clear(self): for row in range(1,self.grid.rowCount()): for col in (0,1,2,3): item = self.grid.itemAtPosition(row,col) if item is not None: item.widget().hide() item.widget().deleteLater() def updateViewFromModel(self): #print 'CColumnGroupListView.updateViewFromModel' import functools self.clear() for row in range(1,len(self.model.__dict__['_value'])+1): value = self.model.__dict__['_value'][row-1] #print 'CColumnGroupListView.updateViewFromModel',value if self.editable and len(value.columnGroupType)>0: cb = QtGui.QCheckBox(self) cb.setChecked(bool(value.selected)) self.connect(cb,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.handleClick,row)) else: cb = CCP4Widgets.CCheckBoxUneditable(parent=self) self.grid.addWidget(cb,row,0) self.grid.addWidget(QtGui.QLabel(value.dataset.__str__(),self),row,1) self.grid.addWidget(QtGui.QLabel(value.columnGroupType.__str__(),self),row,2) self.grid.addWidget(QtGui.QLabel(value.columnListStr(),self),row,3) def handleClick(self,row,status): print 'handleClick',row,status self.model.blockSignals(True) self.model.__dict__['_value'][row-1].selected.set(status) self.model.blockSignals(False) def updateModelFromView(self): for row in range(1,self.grid.rowCount()): #print 'updateModelFromView', row,self.grid.itemAtPosition(row,0).widget() self.model.__dict__['_value'][row-1].selected.set(self.grid.itemAtPosition(row,0).widget().isChecked()) def numberSelected(self): n = 0 for row in range(1,len(self.model.__dict__['_value'])+1): if self.grid.itemAtPosition(row,0).widget().isChecked(): n += 1 return n class CGenericReflDataFileView(CMtzDataFileView): MODEL_CLASS = CCP4XtalData.CGenericReflDataFile def getMenuDef(self): viewSubMenu = ['View','view_ViewHKL','view_text'] if self.editable: menu = ['clear',viewSubMenu,'sep','copy','paste','help'] else: if self.role is not None and self.role == 'output': menu = [viewSubMenu,'sep','copy','editLabel','export','help'] else: menu = [viewSubMenu,'sep','copy','export','help'] if self._stacked: menu.insert(0,'handleStack') return menu class CSelectColumnsWidget(QtGui.QDialog): ERROR_CODES = { 300 : { 'description' : 'There is no data of required type in MTZ file' } } def __init__(self,parent=None,model=None,applyNow=False,filename=None): import os,functools import CCP4TaskWidget, CCP4DataManager QtGui.QDialog.__init__(self,parent) self.model = model baseName = str(self.model.baseName) if len(baseName)>0: baseName = os.path.splitext(baseName)[0] self.setModal(True) self.setWindowModality(QtCore.Qt.WindowModal) self.setWindowTitle('Select columns from MTZ file') self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(3,3,3,3) self.layout().setSpacing(3) self.setMaximumWidth(CCP4TaskWidget.WIDTH) self.layout().addWidget(QtGui.QLabel('Extracting data from the file: '+filename,self)) self.layout().addWidget(QtGui.QLabel('Select one set of columns for '+self.model.qualifiers('toolTip'),self)) self.buttonGroup = QtGui.QButtonGroup(self) self.buttonGroup.setExclusive(True) columnGroupModelList = self.model.columnGroup() columnGroupsInFile = self.model.fileContent.getColumnGroups() #for item in columnGroupModelList: # print 'CSelectColumnsWidget.__init__ columnGroupModelList',item.get() colGpType = self.model.__class__.__name__[1:-8] orLabel = 'Select ' requiredContent = self.model.requiredContent() #print 'CSelectColumnsWidget.__init__ requiredContent',requiredContent if requiredContent is not None and 0 in requiredContent: requiredContent = None contentFlag = 0 checked = True nOfRequiredContent = 0 for columnGroupModel in columnGroupModelList: contentFlag += 1 columnGroupList = [] for item in columnGroupsInFile: if item.columnGroupType == colGpType and item.contentFlag == contentFlag: columnGroupList.append(item) #for item in columnGroupList: # print 'CSelectColumnsWidget.__init__ columnGroupList',contentFlag,item.get() if (requiredContent is None or contentFlag in requiredContent) and len(columnGroupList)>0: #print 'CSelectColumnsWidget.__init__ columnGroupModel',columnGroupModel nOfRequiredContent += 1 self.layout().addWidget(QtGui.QLabel( orLabel + columnGroupModel.qualifiers('guiLabel').__str__().lower(),self)) columnGroupWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=columnGroupModel)(self,model=columnGroupModel) columnGroupWidget.setObjectName('contentFlag_'+str(contentFlag)) radio = QtGui.QRadioButton(self) self.buttonGroup.addButton(radio) self.buttonGroup.setId(radio,contentFlag) radio.setChecked(checked) columnGroupWidget.layout().addWidget(radio,0,0) columnGroupWidget.drawColumns(len(columnGroupList)>1) columnGroupWidget.populateColumns(columnGroupList) self.layout().addWidget(columnGroupWidget) orLabel = '..or ' checked= False #print 'CMiniMtzDataFileView.handleBrowserOpenFile',type(self.model),self.model.CONTENTS if nOfRequiredContent == 0: #There is no content of required type raise CException(self.__class__,300) if len(self.model.CONTENTS['subType']['qualifiers']['enumerators'])>0: line = QtGui.QHBoxLayout() subTypeWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=self.model.subType)(self,model=self.model.subType,qualifiers=self.model.CONTENTS['subType']['qualifiers']) subTypeWidget.updateViewFromModel() line.addWidget(QtGui.QLabel('This is',self)) line.addWidget(subTypeWidget) line.addStretch(1) self.layout().addLayout(line) line = QtGui.QHBoxLayout() self.annotationWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=self.model.annotation)(self,model=self.model.annotation) self.annotationWidget.updateViewFromModel() line.addWidget(QtGui.QLabel('File label',self)) line.addWidget(self.annotationWidget) self.layout().addLayout(line) self.setAnnotation() self.connect(self.buttonGroup,QtCore.SIGNAL('buttonClicked(int)'),self.setAnnotation) for columnGroupWidget in self.findChildren(CProgramColumnGroupView): self.connect(columnGroupWidget.model,QtCore.SIGNAL('dataChanged'),self.setAnnotation) buttonLine = QtGui.QHBoxLayout() buttonLine.addStretch(.5) buttonBox = QtGui.QDialogButtonBox(self) button = buttonBox.addButton(QtGui.QDialogButtonBox.Apply) self.connect(button,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('apply'))) button = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(button,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('cancel'))) button = buttonBox.addButton(QtGui.QDialogButtonBox.Help) self.connect(button,QtCore.SIGNAL('released()'),self.handleHelp) buttonLine.addWidget(buttonBox) buttonLine.addStretch(.5) self.layout().addLayout(buttonLine) if not applyNow: self.show() else: self.emit(QtCore.SIGNAL('apply')) # Correct column types - wrong column labels def setAnnotation(self,clickedInt=None): import os baseName = str(self.model.baseName) if len(baseName)>0: baseName = os.path.splitext(baseName)[0] rv = self.getSelection() if rv is None: return contentFlag,i2Names,dataset,colLabels = self.getSelection() label = '' for item in colLabels: label = label + item + ',' #label =self.model.qualifiers('guiLabel') jN = self.parent().jobNumber() if jN is not None: jN = ' imported by job '+jN else: jN = '' #print 'setAnnotation',baseName,'*',dataset,'*',label,'*',jN #self.model.annotation.set(baseName + ' ' + dataset + '/'+label[0:-1] + jN) self.model.annotation.set(baseName + ': ' + dataset + jN) self.annotationWidget.updateViewFromModel() def getSelection(self): contentFlag = self.buttonGroup.checkedId() columnGroupWidget = self.findChild(CProgramColumnGroupView,'contentFlag_'+str(contentFlag)) if columnGroupWidget is None: return None colLabels = [] for w in columnGroupWidget.widgets: #print 'CSelectColumnsWidget.getSelection currentText',w.currentIndex(),w.currentText() if isinstance(w,CCP4Widgets.CLabel): txt = w.text().__str__().split('/') else: txt = w.currentText().__str__().split('/') colLabels.append(txt[-1]) if len(txt)>1: dataset = txt[0] else: dataset = '' #print 'getSelection',columnGroupWidget.model.columnGroupNames(),dataset,colLabels return contentFlag,columnGroupWidget.model.columnGroupNames(),dataset,colLabels def handleHelp(self): CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='data_files')