""" CCP4I1Projects.py: CCP4 GUI Project Copyright (C) 2016 STFC 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. """ """ Liz Potterton Jan 2016 - Classes for importing and displaying CCP4i projects """ """ To interpret i1 project def files we need to access ccp4i/etc/modules.def ccp4i/tasks/*.def """ import os,re,time,sys,traceback,copy,functools import CCP4Utils, CCP4ProjectWidget, CCP4WebBrowser, CCP4WebView, CCP4TextViewer, CCP4File from CCP4Modules import * from CCP4TaskManager import TASKMANAGER from CCP4ErrorHandling import * from PyQt4 import QtCore,QtGui from lxml import etree # CI1ProjectManager -> CI1Project -> CI1Job ->CI1File is hierarchy of # data classes to load and hold data from i1 directories.def and database.def files # They have appropriate 'get' methods to support the CI1ProjectWidget (ie the Qt tree view) # I1PROJECTMANAGER is function to access the hierarchy def CI1PREFERENCES(): if CI1Preferences.insts is None: p = CI1Preferences() return CI1Preferences.insts def isAlive(qobj): import sip try: sip.unwrapinstance(qobj) except RuntimeError: return False return True def splitDefLine(line): if len(line) == 0 or line[0] in ['#','@']: return None,'',None key,value = line.split(None,1) if value[0] == '_': return line.split(None,2) else: return key,'',value def readI1DefFile(fileName,loadTypes=False,diagnostic=False): metaData = {} params = {} err = CErrorReport() try: text = CCP4Utils.readFile(fileName) except: err.append(CI1ProjectViewer,102,details=fileName,stack=False) return metaData,params,err for line in text.split('\n'): try: if line.startswith('#CCP4I'): metaData[line.split()[1]] = line.split(None,2)[2] elif line.startswith('#'): pass elif line.startswith('@'): #Beware included files on the script def files #@ [FileJoin [GetEnvPath CCP4I_TOP] tasks harvest.def] importFile = 'UNKNOWN' importTask= 'UNKNOWN' try: importTask = line.replace(']','').split()[-1] importFile = os.path.join(CCP4Utils.getCCP4Dir(),'share','ccp4i','tasks',importTask) importMetaData,importParams = readI1DefFile(importFile) params.update(importParams) except Exception as e: print 'Failed loading subsiduary task def file',line err.append(CI1ProjectViewer,110,details='For task: '+importTask+' file: '+importFile,stack=False) else: # Extract param name words = line.split() #print 'words',words if len(words)>=2: if words[0].count(','): pname,idx = words[0].split(',') idx = int(idx) else: pname = words[0] idx = -1 # Extract param value resplit = re.search(r'(.*)"(.*)"(.*)',line) if resplit is not None: value = resplit.groups()[1] else: value = words[-1] # Create params item if not params.has_key(pname): if idx>=0: params[pname] = [ None , [] ] else: params[pname] = [ None , None ] # Set param type if possible if params[pname][0] is None and len(words) > 2 and words[1].startswith('_'): params[pname][0] = words[1] # Set param value - beware items may be missing from list if idx >= 0: while len(params[pname][1])0 or self.annotation is not None) def getEtree(self): #print 'CI1TreeItemProject.getEtree',self.annotation,self.tagList ele = etree.Element('project') ele.set('name',self.refProject) if self.annotation is not None: e = etree.Element('annotation') e.text = str(self.annotation) ele.append(e) if len(self.tagList)>0: eL = etree.Element('tagList') for item in self.tagList: e = etree.Element('tag') e.text = item eL.append(e) ele.append(eL) if self.machineTime is not None: e = etree.Element('lastTaskTime') e.text = str(self.machineTime ) ele.append(e) return ele def loadDummy(self): jItem = CI1TreeItemJob(parent=self, info = { 'jobid' : '0', 'jobnumber' : '0', 'taskname' : 'No jobs in project', 'jobtitle' : 'dummy', 'evaluation' : 'Unknown', 'status' : 'Failed', 'finishtime' : time.time(), 'parentjobid' : None }) self.appendChildJob(jItem) def loadDatabase(self): #print 'loadDatabase',self.directory dbFile = os.path.normpath(os.path.join(self.directory,'CCP4_DATABASE','database.def')) print 'Loading database file:',dbFile metaData,params,err = readI1DefFile(dbFile) if err.maxSeverity()>SEVERITY_WARNING: print err.report() return err if params.get('NJOBS',None) is None: return CErrorReport(self.__class__,101,details=dbFile,stack=False) self.nJobs =int( params['NJOBS'][1] ) #print 'loadDatabase nJobs',self.nJobs lastTime = 0 self.childJobs = [] for nJ in range(1,self.nJobs+1): if params['TASKNAME'][1][nJ] is None: pass else: lastTime = max(lastTime,CCP4Utils.safeFloat(params['DATE'][1][nJ],0)) info = { 'jobid' : str(nJ), 'jobnumber' : str(nJ), 'taskname' : params['TASKNAME'][1][nJ], 'jobtitle' : params['TITLE'][1][nJ], 'evaluation' : 'Unknown', 'status' : CI1TreeItemJob.STATUSCONV.get(params['STATUS'][1][nJ],'Pending'), 'finishtime' : CCP4Utils.safeFloat(params['DATE'][1][nJ]), 'parentjobid' : None } jItem = CI1TreeItemJob(self,info=info) self.appendChildJob(jItem) # Load the job def file to try to sort out file types #jobFile = os.path.join(dirName,str(nJ)+'_'+params['TASKNAME'][nJ]+'.def') #jobMetaData,jobParams,err = readI1DefFile(jobFile) inputFiles = self.extractFileList(params['INPUT_FILES'][1][nJ],params['INPUT_FILES_DIR'][1][nJ],jItem,input=1) outputFiles = self.extractFileList(params['OUTPUT_FILES'][1][nJ],params['OUTPUT_FILES_DIR'][1][nJ],jItem) #print 'loadDatabase outputFiles',self.jobs[-1].outputFiles if lastTime>0: self.lastTaskTime = CCP4ProjectWidget.formatDate(lastTime) self.broken = 2 return CErrorReport() def editDatabaseDef(self,resetDir=True,movedFiles={}): err = CErrorReport() dbFile = os.path.normpath(os.path.join(self.directory,'CCP4_DATABASE','database.def')) #print 'editDatabaseDef dbFile',dbFile try: lineList = CCP4Utils.readFile(dbFile).split('\n') except: err.append(self.__class__,101,dbFile) return err done = False if resetDir: for ii in range(len(lineList)): if resetDir and lineList[ii].startswith('#CCP4I PROJECT'): words = lineList[ii].split(None,3) lineList[ii] = '#CCP4I PROJECT '+str(self.refDbIndex)+' '+str(self.directory) done = True break if len(movedFiles)>0: for ii in range(len(lineList)): if lineList[ii].startswith('INPUT_FILES'): nJ = lineList[ii].split()[0][12:] if movedFiles.get(nJ,None) is not None: text = 'INPUT_FILES,'+nJ+' "'+movedFiles[nJ][0] for f in movedFiles[nJ]: text = text + ' '+f lineList[ii] = text+'"' if done: CCP4Utils.backupFile(dbFile,delete=True) text = '' for line in lineList: text = text + line + '\n' #print 'editDatabaseDef',text try: CCP4Utils.saveFile(dbFile,text=text) except: err.append(self.__class__,102,dbFile) else: err.append(self.__class__,103) return err def extractFileList(self,fStr,dStr,parentJob,input=0): fileObjList = [] if fStr is not None: fList = fStr.split() else: fList = [] if dStr is not None: dList = dStr.split() else: dList = [] #print 'extractFileList',type(fList),fList,type(dList),dList for nF in range(min(len(fList),len(dList))): f = fList[nF].strip('"') d = dList[nF].strip('"') if d == 'FULL_PATH': import_ = True else: import_= None ext = os.path.splitext(f)[1] if ext == '.pdb': fType = 2 elif ext == '.mtz': fType = 4 else: fType = 0 broken = 0 if d == 'FULL_PATH': fileName = f elif d == self.refProject: fileName = f if os.path.exists(os.path.join(self.directory,f)): broken = 1 else: broken = -1 else: pObj = self.model().getProject(d) if pObj is not None: fileName = os.path.join(pObj.directory,f) else: fileName = d+'/'+f broken = -1 if broken == 0: if fileName is None: self.broken = -1 elif not os.path.exists(fileName): self.broken = -1 else: self.broken = 1 infoList = [ '0','0',import_,fType , fileName, fileName ] fItem = CI1TreeItemFile(parentJob,infoList,broken=broken,input=input) if input: parentJob.appendChildInputFile(fItem) else: parentJob.appendChildOutputFile(fItem) return fileObjList def data(self,column,role): if role == QtCore.Qt.DisplayRole: if column == 0: return self.projectName elif column == 1 and self.lastTaskTime is not None: return self.lastTaskTime elif role == QtCore.Qt.DecorationRole: if column == 0: return CCP4ProjectWidget.jobIcon('ccp4') elif role == QtCore.Qt.ToolTipRole: if column == 0: text = 'Directory: ' + str(self.directory) if self.annotation is not None: text = text + '\nAnnotation: '+self.annotation if len(self.tagList)>0: text = text + '\nTags: '+ self.tagList[0] for t in self.tagList[1:]: text = text + ', ' + t return QtCore.QVariant(text) elif role == QtCore.Qt.UserRole: if column == 0: return self.identifier elif role == QtCore.Qt.FontRole: return CCP4ProjectWidget.CTreeItemJob.boldFont elif role == QtCore.Qt.ForegroundRole: if self.broken<0: return QtCore.QVariant(QtGui.QBrush(QtCore.Qt.red)) #else: # return QtCore.QVariant(QtGui.QBrush(QtCore.Qt.green)) else: return QtCore.QVariant() def canFetchMore(self): #print 'CI1TreeItemProject.canFetchMore',self.broken,self.broken in [0,1] return (self.broken in [0,1]) def fetchMore(self): self.loadDatabase() def mimeData(self): #print 'CI1TreeItemProject.mimeData' from lxml import etree root = etree.Element('name') root.text = self.getProjectName() encodedData = QtCore.QByteArray() encodedData.append(etree.tostring(root,pretty_print=False)) mimeData = QtCore.QMimeData() mimeData.setData('I1Project',encodedData) return mimeData class CI1TreeItemJob(CCP4ProjectWidget.CTreeItemJob): STATUSCONV = { 'FINISHED' : 'Finished', 'FAILED' : 'Failed' } def __init__(self,parent=None,info={}): CCP4ProjectWidget.CTreeItemJob. __init__(self,parent=parent,info=info) self.finished = info.get('status','blah') == 'Finished' def data(self,column,role): if role == QtCore.Qt.DisplayRole: if column == 0: return self.name elif column == 1: return QtCore.QVariant(self.dateTime) elif role == QtCore.Qt.UserRole: if column == 0: return self.identifier elif role == QtCore.Qt.DecorationRole: if column == 0: return self.status return QtCore.QVariant() def setName(self,jobTitle=None): try: self.name = QtCore.QVariant( self.jobNumber +' '+ TASKMANAGER().getI1TaskTitle(self.taskName) ) except: self.name = QtCore.QVariant(self.jobNumber) print 'Error in CI1TreeItemJob.setName',self.jobNumber,'*',self.taskName,'*',TASKMANAGER().getI1TaskTitle(self.taskName) def logFile(self): log = os.path.join(self.parent().directory,str(self.jobNumber)+'_'+str(self.taskName)+'.log') if os.path.exists(log+'.html'): return log+'.html' elif os.path.exists(log): return log else: return None class CI1TreeItemFile(CCP4ProjectWidget.CTreeItemFile): def __init__(self,parent=None,infoList=[],displayColumn=0,broken=0,input=0): CCP4ProjectWidget.CTreeItemFile.__init__(self,parent=parent,infoList=infoList,displayColumn=displayColumn) self.broken = broken self.input = input def data(self,column,role): if role == QtCore.Qt.DisplayRole: if column == 0: return self.name elif role == QtCore.Qt.UserRole: if column == 0: return self.identifier elif role == QtCore.Qt.DecorationRole: if column == 0: return self.icon elif role == QtCore.Qt.ForegroundRole: if self.broken<0: return QtCore.QVariant(QtGui.QBrush(QtCore.Qt.red)) elif role == QtCore.Qt.FontRole: if column == 0 and self.input>0: return CCP4ProjectWidget.CTreeItemJob.italicFont return QtCore.QVariant() def setName(self,annotation,mimeType=None,maxChar=40,displayJobNumber=False): #print 'CI1TreeItemFile.setName',annotation self.name = QtCore.QVariant( annotation ) def filePath(self): projDir = self.parent().parent().directory #print 'CI1TreeItemFile.filePath',projDir f = os.path.join(projDir,self.fileName) if os.path.exists(f): return f else: return None def mimeData(self): fN = self.filePath() if fN is None: return None urlList = [QtCore.QUrl()] urlList[0].setPath( fN ) mimeData = QtCore.QMimeData() mimeData.setUrls(urlList) #print 'CI1TreeItemFile.mimeData',fN return mimeData class CI1ProjectModel(QtCore.QAbstractItemModel): COLUMNS = ['name','date'] ERROR_CODES = { 101 : { 'description' : 'Failed reading directories.def file'}, 102 : { 'description' : 'Failed to find and update directory in directories.def file'}, 103 : { 'description' : 'Failed writing directories.def file'} } def __init__(self): QtCore.QAbstractItemModel.__init__(self) self.rootItem=CI1TreeItemFolder(self,{ 'name' : 'root' } ) self.loadErrorReport = CErrorReport() self.initialised = False self.sourceFile = None def resetAll(self,args): self.beginResetModel() self.rootItem = CCP4ProjectWidget.CTreeItemProject(self,['-1','root']) self.setupModel() self.endResetModel() def loadSupplement(self,fileName=None): if fileName is None: fileName = os.path.join(CCP4Utils.getDotDirectory(),'i1supplement','temporary.xml') if os.path.exists(fileName): f = CCP4File.CI2XmlDataFile(fullPath=fileName) root = f.getBodyEtree() if root.find('folderList') is not None: for fEle in root.find('folderList').findall('folder'): fItem = CI1TreeItemFolder(self.rootItem,tree=fEle) self.rootItem.appendChildFolder(fItem) if root.find('projectList') is not None: #print 'loadSupplement',root.find('projectList').findall('project') for pEle in root.find('projectList').findall('project'): pItem = CI1TreeItemProject(self.rootItem,tree=pEle) self.rootItem.appendChildProject(pItem) def saveStatus(self): self.saveSupplement() CI1PREFERENCES().save() def saveSupplement(self,fileName=None): if fileName is None: if self.sourceFile is not None and os.access(os.path.split(self.sourceFile)[0], os.W_OK|os.X_OK): fileName = os.path.join(os.path.split(self.sourceFile)[0],'i2supplement.xml') else: fileName = os.path.join(CCP4Utils.getDotDirectory(),'i1supplement','temporary.xml') #print 'CI1ProjectModel.saveSupplement',fileName f = CCP4File.CI2XmlDataFile(fullPath=fileName) if not os.path.exists(fileName): f.header.setCurrent() f.header.function='I1SUPPLEMENT' f.header.comment = self.sourceFile body = etree.Element('body') fEleList = etree.Element('folderList') body.append(fEleList) for fItem in self.rootItem.childFolders: fEleList.append(fItem.getEtree()) pEleList = etree.Element('projectList') for pItem in self.rootItem.childProjects: if pItem.hasAnnotation(): pEleList.append( pItem.getEtree()) if len(pEleList)>0: body.append(pEleList) f.saveFile(bodyEtree=body) def loadDirectoriesDefFile(self,fileName=None): err = CErrorReport() metaData,params,err = readI1DefFile(fileName) if err.maxSeverity()>SEVERITY_WARNING or not params.has_key('N_PROJECTS'): return CErrorReport(self.__class__,101,details = fileName,stack=False) nProjects = CCP4Utils.safeInt(params.get('N_PROJECTS')[1]) for nP in range(1,nProjects+1): pObj = self.getProject(params['PROJECT_ALIAS'][1][nP]) #print 'loadDirectoriesDefFile',params['PROJECT_ALIAS'][1][nP],pObj if pObj is None: infoList= [params['PROJECT_ALIAS'][1][nP],params['PROJECT_ALIAS'][1][nP]] pObj = CI1TreeItemProject(self.rootItem,infoList=infoList) self.rootItem.appendChildProject(pObj) pObj.refDbIndex = nP pObj.setDirectory(os.path.split(params['PROJECT_DB'][1][nP])[0]) pObj.loadDummy() #print 'I1 projects listed in database:',self._lastLoadedProjectIds self.sourceFile = fileName return err def editDirectoriesDefFile(self,projectDbIndex=None,directory = None): err = CErrorReport() try: lineList = CCP4Utils.readFile(self.sourceFile).split('\n') except: err.append(self.__class__,101,self.sourceFile) return err #print 'editDirectoriesDefFile',projectDbIndex,directory if projectDbIndex is not None and directory is not None: s1 = 'PROJECT_DB,'+str(projectDbIndex) s2 = 'PROJECT_PATH,'+str(projectDbIndex) done = False for ii in range(len(lineList)): key,keyType,value = splitDefLine(lineList[ii]) #print 'editDirectoriesDefFile',ii,key,'*',keyType,'*',value if key is None: pass elif key == s1: lineList[ii] = key + ' ' + keyType + ' ' + os.path.join( directory , 'CCP4_DATABASE' ) done = True elif key == s2: lineList[ii] = key + ' ' + keyType + ' ' + directory done = True if done: CCP4Utils.backupFile(self.sourceFile,delete=True) text = '' for line in lineList: text = text + line +'\n' #print 'editDirectories.def',text try: CCP4Utils.saveFile(self.sourceFile,text=text) except Exception as e: err.append(self.__class__,103,self.sourceFile) else: err.append(self.__class__,102,self.sourceFile) return err def addFolder(self,name,parent=None): if parent is None: parent = self.rootItem fItem = CI1TreeItemFolder(parent=parent, info = { 'name' : name} ) self.beginInsertRows(QtCore.QModelIndex(),len(self.rootItem.childFolders),len(self.rootItem.childFolders)) parent.appendChildFolder(fItem) self.endInsertRows() return fItem def deleteFolder(self,folderId): fItem = self.getFolder(folderId) if fItem is None: print 'Failed to find and delete folder',folderId self.beginResetModel() fItem.parent().removeChildFolder(folderId) self.endResetModel() def canFetchMore(self,parent): #print 'CI1ProjectModel.canFetchMore',parent,parent.isValid(),parent.internalPointer() if not parent.isValid(): return False else: return parent.internalPointer().canFetchMore() def fetchMore(self,parent): # Load the job/files for any opened project in the tree model if not self.canFetchMore(parent): return parentNode = parent.internalPointer() parentNode.fetchMore() 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) def rowCount(self, parent): #if parent.column() > 0: # return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def columnCount(self,parent): return 2 def data(self,index,role): if not index.isValid(): return None item = index.internalPointer() return item.data(index.column(),role) def headerData(self,section,orientation,role=QtCore.Qt.DisplayRole): if orientation == QtCore.Qt.Horizontal: if role==QtCore.Qt.DisplayRole: if section == 0: return QtCore.QVariant('Project/Job/File') elif section == 1: return QtCore.QVariant('Date') return QtCore.QVariant() def flags(self,modelIndex): if not modelIndex.isValid(): return QtCore.Qt.NoItemFlags ic = modelIndex.column() if ic == 0: return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsDropEnabled else: return QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled def nodeFromIndex(self, index): if index is not None and index.isValid(): #print 'nodeFromIndex',index,index.internalPointer() return index.internalPointer() else: return self.rootItem def getFolder(self,fId): return self.getProject(fId) def getProject(self,pId): indexList = self.match(self.index(0,0,QtCore.QModelIndex()),QtCore.Qt.DisplayRole,QtCore.QVariant(pId),1, QtCore.Qt.MatchExactly|QtCore.Qt.MatchWrap|QtCore.Qt.MatchRecursive) #print 'getProjectTreeItem',pId,indexList if len(indexList)>0: return indexList[0].internalPointer() else: return None def modelIndexFromProject(self,pId): indexList = self.match(self.index(0,0,QtCore.QModelIndex()),QtCore.Qt.DisplayRole,QtCore.QVariant(pId),1, QtCore.Qt.MatchExactly|QtCore.Qt.MatchWrap|QtCore.Qt.MatchRecursive) if len(indexList)>0: return indexList[0] else: return None def getJob(self,pId,jId): indexList = self.match(self.index(0,0,QtCore.QModelIndex()),QtCore.Qt.DisplayRole,QtCore.QVariant(pId),1, QtCore.Qt.MatchExactly|QtCore.Qt.MatchWrap|QtCore.Qt.MatchRecursive) #print 'getJob',pId,jId,indexList if len(indexList)==0: return None jList = self.match(self.index(0,0,indexList[0]),QtCore.Qt.UserRole,QtCore.QVariant(jId),1, QtCore.Qt.MatchExactly|QtCore.Qt.MatchWrap ) if len(jList)>0: return jList[0].internalPointer() else: return None def getJob1(self,jId): jList = self.match(self.index(0,0,QtCore.QModelIndex()),QtCore.Qt.UserRole,QtCore.QVariant(jId),1, QtCore.Qt.MatchExactly|QtCore.Qt.MatchWrap ) if len(jList)>0: return jList[0].internalPointer() else: return None class CI1ProjectView(QtGui.QTreeView): def __init__(self,parent=None): QtGui.QTreeView.__init__(self,parent) self.setObjectName('i1ProjectWidget') self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDragDropMode(QtGui.QAbstractItemView.InternalMove) self.setExpandsOnDoubleClick(False) self.setRootIsDecorated(True) self.setIconSize(QtCore.QSize(16,16)) self.setEditTriggers(QtGui.QAbstractItemView.EditKeyPressed) #self.setFocusPolicy(QtCore.Qt.NoFocus) self.setToolTip('Right mouse click for options to view jobs and files') self.setAlternatingRowColors(PREFERENCES().TABLES_ALTERNATING_COLOR) self.connect(PREFERENCES().TABLES_ALTERNATING_COLOR,QtCore.SIGNAL('dataChanged'),self.resetAlternatingRowColors) self.forceUpdate = sys.platform.count('inux') or sys.platform.count('arwin') #print 'CProjectView.__init__ forceUpdate',self.forceUpdate def update(self): if not self.forceUpdate: return #print 'CProjectView.update' #QtGui.QTreeView.update(self) # Why am I doing this??? This is broken on Windows # Maybe this fixes the failure to update on Linux frameRect = self.frameRect() self.setDirtyRegion(QtGui.QRegion(frameRect.x(),frameRect.y(),frameRect.width(),frameRect.height())) #print 'from CProjectView.update' def mousePressEvent(self,event=None): #print 'mousePressEvent' if event.button() == QtCore.Qt.RightButton: self.emit(QtCore.SIGNAL('rightMousePress'),event) event.accept() return else: mousePressX = event.x() modelIndex = self.indexAt(event.pos()) r = self.visualRect(modelIndex) if r.isValid(): indent = 0 #print 'mousePressEvent mousePressX',mousePressX,'left',r.left(),(r.left() + self.iconSize().width() + 4) if mousePressX >r.left()+indent and mousePressX < (r.left() + self.iconSize().width() + 4 + indent): self.startDrag(modelIndex=modelIndex) event.accept() return node = self.model().nodeFromIndex(modelIndex) if node.isJob(): self.emit(QtCore.SIGNAL('jobClicked'),modelIndex) else: if isinstance(node,CI1TreeItemFile): self.emit(QtCore.SIGNAL('fileClicked'),node.filePath()) event.accept() return #print 'calling QTreeView.mousePressEvent' QtGui.QTreeView.mousePressEvent(self,event) def nodeFromEvent(self,event): modelIndex = self.indexAt(QtCore.QPoint(event.x(),event.y())) col = self.model().COLUMNS[modelIndex.column()] return modelIndex,self.model().nodeFromIndex(modelIndex),col return [None,None,None,indx] def selectRow(self,modelIndex=None): #print 'CProjectView.selectRow',modelIndex.row() sel = QtGui.QItemSelection( modelIndex.sibling(modelIndex.row(),0) , modelIndex.sibling(modelIndex.row(),2) ) self.selectionModel().select(sel,QtGui.QItemSelectionModel.ClearAndSelect) #print 'CProjectView.selectRow DONE' def startDrag(self,dropActions=None,modelIndex=None): #print 'CI1ProjectView.startDrag' if modelIndex is None: modelIndex = self.currentIndex() if modelIndex is None: return node = self.model().nodeFromIndex(modelIndex) #print 'startDrag',node if isinstance(node,(CI1TreeItemFolder,CI1TreeItemProject,CI1TreeItemFile)): mimeData = node.mimeData() if mimeData is None: return drag = QtGui.QDrag(self) drag.setMimeData(mimeData) icon = node.data(0,QtCore.Qt.DecorationRole) if icon is not None: try: pixmap = icon.pixmap(18,18) drag.setHotSpot(QtCore.QPoint(9,9)) drag.setPixmap(pixmap) except: pass drag.exec_(QtCore.Qt.CopyAction) def dragEnterEvent(self,event): if event.mimeData().hasFormat('I1Project') or event.mimeData().hasFormat('I1Folder'): event.accept() else: event.ignore() def dragMoveEvent(self,event): if not self.indexAt(event.pos()).isValid(): return event.accept() targetItem = self.indexAt(event.pos()).internalPointer() if not isinstance(targetItem,CI1TreeItemFolder): event.ignore() return #if event.mimeData().hasFormat('project') and dropItem is not None: if event.mimeData().hasFormat('I1Project') or event.mimeData().hasFormat('I1Folder') : event.setDropAction(QtCore.Qt.MoveAction) event.accept() else: event.ignore() def dropEvent(self,event): #print 'dropEvent',self.indexAt(event.pos()) if not self.indexAt(event.pos()).isValid(): # Attempting to drop into space - i.e. move object to root level targetItem = self.model().rootItem else: targetItem = self.indexAt(event.pos()).internalPointer() #print 'dropEvent',targetItem if not isinstance(targetItem,CI1TreeItemFolder): event.setDropAction(QtCore.Qt.IgnoreAction) event.ignore() return if event.mimeData().hasFormat('I1Project') or event.mimeData().hasFormat('I1Folder'): pass else: event.setDropAction(QtCore.Qt.IgnoreAction) event.ignore() return targetFolderId = self.item2FolderId(targetItem) #print 'dropEvent targetFolder',targetFolderId failed = False try: self.model().beginResetModel() if event.mimeData().hasFormat('I1Project'): movedItemName = self.event2MimeData(event,'I1Project')['name'] movedItem = self.model().getProject(movedItemName) err = movedItem.parent().removeChildProject(movedItemName) #print 'dropEvent',movedItem.parent(),movedItem.parent().childProjects if len(err)>0: raise err #print 'dropEvent movedItem childJobs',movedItem.childJobs movedItem.setParent(targetItem) targetItem.appendChildProject(movedItem) #print 'dropEvent move project',movedItemName,'to',targetItem.name else: movedItemName = self.event2MimeData(event,'I1Folder')['name'] movedItem = self.model().getFolder(movedItemName) if targetItem == movedItem.parent(): print 'drag-n-drop to same place' failed = True err = movedItem.parent().removeChildFolder(movedItemName) if len(err)>0: print 'Failed removing folder from parent',movedItem failed = True movedItem.setParent(targetItem) targetItem.appendChildFolder(movedItem) #print 'dropEvent move folder',movedItemName,'to',targetItem.name except CException as e: print e.report() failed = True except Exception as e: print 'Failed to move project',e failed = True self.model().endResetModel() if failed: event.setDropAction(QtCore.Qt.IgnoreAction) event.ignore() else: event.setDropAction(QtCore.Qt.MoveAction) event.accept() def item2FolderId(self,item): if item is None: return None folderId = str(item.data(0,QtCore.Qt.UserRole).toString()) #print 'item2FolderId',folderId return folderId def event2MimeData(self,event,label): text = str(event.mimeData().data(label).data()) #print 'event2MimeData',text tree = etree.fromstring(text) params = { } if tree.tag != 'root' : params[str(tree.tag)]=str(tree.text) for child in tree: params[child.tag] = child.text #print 'event2MimeData',params return params def resetAlternatingRowColors(self): self.setAlternatingRowColors(PREFERENCES().TABLES_ALTERNATING_COLOR) class CI1ProjectWidget(QtGui.QFrame): MARGIN = 0 def __init__(self,parent=None): #print 'CI1ProjectWidget.__init__' QtGui.QFrame.__init__(self,parent) layout = QtGui.QVBoxLayout() layout.setMargin(CI1ProjectWidget.MARGIN) layout.setContentsMargins(CI1ProjectWidget.MARGIN,CI1ProjectWidget.MARGIN, CI1ProjectWidget.MARGIN,CI1ProjectWidget.MARGIN) layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize) self.setLayout(layout) #self.tab = QtGui.QTabWidget(self) #self.layout().addWidget(self.tab) model = CI1ProjectModel() self.projectView=CI1ProjectView(self) self.projectView.setModel(model) self.connect(model,QtCore.SIGNAL('redraw'),self.projectView.update) self.projectView.model().reset() self.projectView.setColumnWidth(0,300) self.connect(self.projectView,QtCore.SIGNAL('rightMousePress'),self.showPopup) #self.tab.addTab(self.projectView,'Job list') self.layout().addWidget(self.projectView) self.connect(self.projectView.selectionModel(),QtCore.SIGNAL('selectionChanged (const QItemSelection& , const QItemSelection&)'),self.handleSelectionChanged) #self.connect(self.projectView,QtCore.SIGNAL('doubleClicked(const QModelIndex &)'),self.handleDoubleClick) #self.doubleClicked = None self.popupMenu = QtGui.QMenu(self) #print 'DONE CI1ProjectWidget.__init__' def model(self): return self.projectView.model() def showPopup(self,event): modelIndex,node,column = self.projectView.nodeFromEvent(event) #print 'CI1ProjectWidget.showPopup',node.getName(),column position = QtCore.QPoint(event.globalX(),event.globalY()) self.popupMenu.setTitle(node.getName()) self.popupMenu.clear() if node.isFolder(): action = self.popupMenu.addAction('Delete folder') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('deleteFolder'),node.name)) if len(node.childFolders) + len(node.childProjects) > 0: action.setEnabled(False) elif node.isProject(): action = self.popupMenu.addAction('Reload project') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('reloadProject'),node.refProject)) ''' action = self.popupMenu.addAction('Annotate project') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('annotateProject'),node.refProject)) ''' action = self.popupMenu.addAction('Make CCP4i2 project') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('makeI2Project'),node.refProject)) action = self.popupMenu.addAction('Associate with CCP4i2 project') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('associateI2Project'),node.refProject)) action = self.popupMenu.addAction('Collect input files') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('collectProject'),node.refProject)) if os.path.exists(os.path.join(node.directory,'IMPORTED_FILES')): action.setEnabled(False) action = self.popupMenu.addAction('Find project directory') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('findProject'),node.refProject)) if node.broken>=0: action.setEnabled(False) elif node.isJob(): popupViewSubMenu = self.popupMenu.addMenu('View') action = popupViewSubMenu.addAction('Job results (new style,qtrview)') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('viewInQtrview'),node.logFile())) action = popupViewSubMenu.addAction('Def file') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('viewDefFile'),node.parent().refProject,node.jobId)) action = popupViewSubMenu.addAction('In Coot') if not node.finished: action.setEnabled(False) self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('viewInCoot'),node.parent().refProject,node.jobId)) action = popupViewSubMenu.addAction('In CCP4mg') if not node.finished: action.setEnabled(False) self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('viewInMg'),node.parent().refProject,node.jobId)) elif node.isFile(): action = self.popupMenu.addAction('View') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('viewFile'),node.filePath())) action = self.popupMenu.addAction('Copy') self.connect(action,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.emit,QtCore.SIGNAL('copyFile'),node.filePath())) self.popupMenu.popup(position) def handleSelectionChanged(self,selected,deselected): indices = selected.indexes() if len(indices)>0: try: node = self.projectView.model().nodeFromIndex(indices[0]) #jobId = node.jobId #projectId = node.parent().getProjectId() logFile = node.logFile() except: return else: self.emit(QtCore.SIGNAL('showLogFile'),logFile) class CI1ProjectViewer(CCP4WebBrowser.CMainWindow): Instances = [] MARGIN = 2 ERROR_CODES = { 101 : { 'description' : 'Failed to find/open old CCP4 master directories.def file' }, 102 : { 'description' : 'Failed to find/open old CCP4 def file' }, 103 : { 'description' : 'Failed loading task titles from modules definition file' }, 104 : { 'description' : 'Unknown failure loading project database file' }, 105 : { 'severity' : SEVERITY_WARNING, 'description' : 'Project with this name already loaded - will overwrite' }, 106 : { 'severity' : SEVERITY_WARNING, 'description' : 'Project directory does not exist' }, 107 : { 'description' : 'Folder with this name already exists' }, 110 : { 'severity' : SEVERITY_WARNING, 'description' : 'Failed loading subsiduary task def file' }, 111 : { 'severity' : SEVERITY_WARNING, 'description' : 'Failed parsing line of def file' }, 112 : { 'severity' : SEVERITY_WARNING, 'description' : 'Failed loading CCP4 task def file to extract type info' }, 121 : { 'description' : 'Input file not found' }, 122 : { 'severity' : SEVERITY_OK, 'description' : 'Copied' } } def __init__(self,parent=None,fileName=None): CCP4WebBrowser.CMainWindow.__init__(self,parent) CI1ProjectViewer.Instances.append(self) self.setObjectName('projectViewer') self.preferences = CI1Preferences() CCP4ProjectWidget.CTreeItemJob.TODAY = time.strftime(CCP4ProjectWidget.CTreeItemJob.DATE_FORMAT,time.localtime()) CCP4ProjectWidget.CTreeItemJob.THISYEAR = time.strftime('%y',time.localtime()) self.actionDefinitions = { 'i1_help' : { 'text' : 'Help', 'tip' : 'Help on viewing old CCP4i projects', 'slot' : self.showHelp }, 'show_load_errors' : { 'text' : 'Show load errors', 'tip' : 'List problems in loading CCP4i projects', 'slot' : self.showLoadErrors }, 'i1preferences' : { 'text' : 'Preferences', 'tip' : 'CCP4i project viewer preferences', 'enabled' : True, 'slot' : self.showPreferences }, 'add_folder' : { 'text' : 'Add folder', 'tip' : 'Create a folder to organise projects', 'slot' : self.createFolder } } self.connect(self,QtCore.SIGNAL("destroyed(QObject*)"),CI1ProjectViewer.updateInstances) self.setWindowTitle(self.version+'Old CCP4i Project Viewer') self.layout().setContentsMargins(CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN) self.layout().setSpacing(CI1ProjectViewer.MARGIN) # left side project widget and buttons leftFrame = QtGui.QFrame(self) leftFrame.setLayout( QtGui.QVBoxLayout()) leftFrame.layout().setContentsMargins(CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN,CI1ProjectViewer.MARGIN) leftFrame.layout().setSpacing(CI1ProjectViewer.MARGIN) self._projectWidget = CI1ProjectWidget(self) self._projectWidget.setMinimumSize(QtCore.QSize(370,300)) leftFrame.layout().addWidget(self._projectWidget) #self.connect(self._projectWidget,QtCore.SIGNAL('currentJobChanged'),self.handleCurrentJobChanged) self.connect(self._projectWidget,QtCore.SIGNAL('showLogFile'),self.showLogFile) self.connect(self._projectWidget.projectView,QtCore.SIGNAL('fileClicked'),self.handleFileClicked) self.connect(self._projectWidget,QtCore.SIGNAL('viewInQtrview'),self.viewInQtrview) self.connect(self._projectWidget,QtCore.SIGNAL('viewInCoot'),functools.partial(self.viewInGraphics,'coot')) self.connect(self._projectWidget,QtCore.SIGNAL('viewInMg'),functools.partial(self.viewInGraphics,'ccp4mg')) self.connect(self._projectWidget,QtCore.SIGNAL('viewDefFile'),self.viewDefFile) self.connect(self._projectWidget,QtCore.SIGNAL('collectProject'),self.collectProject) self.connect(self._projectWidget,QtCore.SIGNAL('annotateProject'),self.annotateProject) self.connect(self._projectWidget,QtCore.SIGNAL('makeI2Project'),self.makeI2Project) self.connect(self._projectWidget,QtCore.SIGNAL('associateI2Project'),self.handleAssociateI2Project) self.connect(self._projectWidget,QtCore.SIGNAL('reloadProject'),self.reloadProject) self.connect(self._projectWidget,QtCore.SIGNAL('findProject'),self.findProjectDir) self.connect(self._projectWidget,QtCore.SIGNAL('viewFile'),self.handleFileClicked) self.connect(self._projectWidget,QtCore.SIGNAL('copyFile'),self.copyFile) self.connect(self._projectWidget,QtCore.SIGNAL('deleteFolder'),self.model().deleteFolder) self.rightFrame = QtGui.QFrame(self) self.rightFrame.setLayout(QtGui.QVBoxLayout()) self.qtrButton = QtGui.QPushButton('Show log file including graphs in qtrview',self) self.connect(self.qtrButton,QtCore.SIGNAL('clicked()'),functools.partial(self.viewInQtrview,'CURRENTLOG')) self.rightStack = QtGui.QStackedWidget(self) self.webView= CCP4WebView.CWebView(self,blockLoading=True) self.rightStack.addWidget(self.webView) self.textView = CCP4TextViewer.CTextViewer(self) self.rightStack.addWidget(self.textView) self.rightFrame.layout().addWidget(self.rightStack) self.rightFrame.layout().addWidget(self.qtrButton) # left/right splitter import CCP4TaskWidget self.splitterSizes = [400,CCP4TaskWidget.WIDTH+CCP4TaskWidget.PADDING_ALLOWANCE] mainWidget = QtGui.QSplitter(self) mainWidget.setOrientation(QtCore.Qt.Horizontal) mainWidget.addWidget(leftFrame) mainWidget.addWidget(self.rightFrame) # And set the splitter sizes after show ... self.mainToolBar = CCP4WebBrowser.CToolBar(self,'i1tools','CCP4I1 projects toolbar') self.addToolBar(self.mainToolBar) #self.mainToolBar.extend(['i1preferences','show_load_errors','add_folder']) self.mainToolBar.extend(['show_load_errors','add_folder','i1_help']) self.updateLoadErrorsEnabled() self.setCentralWidget(mainWidget) self.setCurrentLogFile() #print 'to loadDirectoriesDefFile',fileName # Try reading a supplement file supFile = os.path.join(os.path.split(fileName)[0],'i2supplement.xml') if os.path.exists(supFile): self.model().loadSupplement(supFile) self.model().loadDirectoriesDefFile(fileName=fileName) self.model().reset() def model(self): return self._projectWidget.projectView.model() def updateLoadErrorsEnabled(self): enabled = (len(self._projectWidget.projectView.model().loadErrorReport)>0) #print 'updateLoadErrorsEnabled',enabled self.findChild(QtGui.QAction,'show_load_errors').setEnabled(enabled) def showLoadErrors(self): message = 'Errors loading old CCP4i projects:\n' for proj,dir in self._projectWidget.projectView.model().loadErrorProjects: message += '{:30} {:100}\n'.format(proj,dir) self._projectWidget.projectView.model().loadErrorReport.warningMessage(parent=self,windowTitle=self.windowTitle(),message=message) def showHelp(self): WEBBROWSER().loadWebPage(helpFileName='CCP4i1Projects.html',newTab=True) def handleCurrentJobChanged(self,jobId,projectId): #print 'handleCurrentJobChanged',jobId,projectId pass def setCurrentLogFile(self,logFile=None): if logFile is None: colour = 'grey' else: colour = 'black' print 'setCurrentLogFile',colour self.qtrButton.setStyleSheet("QPushButton { color:"+colour +"; }") self.currentLogFile = logFile def showLogFile(self,logFile): #print 'showLogFile',logFile if logFile is None: return elif logFile.endswith('html') or logFile.endswith('htm'): self.webView.load(QtCore.QUrl.fromLocalFile(logFile)) self.rightStack.setCurrentIndex(0) else: self.textView.loadText(CCP4Utils.readFile(logFile)) self.rightStack.setCurrentIndex(1) self.setCurrentLogFile(logFile) def handleFileClicked(self,fileName=None,fileType=None): if fileName is None: return if fileType is None: ext = os.path.splitext(fileName)[1] if ext == '.mtz': fileType = "application/CCP4-mtz" elif ext == '.pdb': fileType = "chemical/x-pdb" else: fileType = "text/plain" if fileType == "application/CCP4-mtz": LAUNCHER().launch('viewhkl',[fileName]) elif fileType == "chemical/x-pdb": self.textView.loadText(CCP4Utils.readFile(fileName)) self.rightStack.setCurrentIndex(1) self.setCurrentLogFile(None) else: self.textView.loadText(CCP4Utils.readFile(fileName)) self.rightStack.setCurrentIndex(1) self.setCurrentLogFile(None) def viewInQtrview(self,logFile=None): if logFile is None: return elif logFile == 'CURRENTLOG': if self.currentLogFile is None: return import copy logFile = copy.deepcopy(self.currentLogFile) if os.path.splitext(logFile)[1] == '.html': logFile = os.path.splitext(logFile)[0] LAUNCHER().launch('logview',[logFile]) def viewInGraphics(self,mode='coot',projectId=None,jobId=None): #print 'viewInGraphics',mode,projectId,jobId jItem = self.model().getJob(projectId,jobId) if jItem is None: print 'ERROR in viewInGraphics - could not find job:',projectId,jobId return fileList = [] for fItem in jItem.childOutFiles: if fItem.broken>0: path = fItem.filePath() if os.path.splitext(path)[1] in ['.mtz','.pdb']: fileList.append(fItem.filePath()) if mode == 'coot' : mode = 'coot0' LAUNCHER().openInViewer(viewer=mode,fileName=fileList) def viewDefFile(self,projectId=None,jobId=None): #print 'viewDefFile',projectId,jobId p = self.model().getProject(projectId) if p is None: print 'ERROR in viewDefFile - could not find project:',projectId return j = self.model().getJob(projectId,jobId) if j is None: print 'ERROR in viewDefFile - could not find job:',projectId,jobId return defFile =os.path.join( p.directory,'CCP4_DATABASE',str(jobId)+'_'+j.taskName+'.def') #print 'viewDefFile',defFile self.textView.loadText(CCP4Utils.readFile(defFile),fixedWidthFont=True) self.rightStack.setCurrentIndex(1) def findProjectDir(self,projectId): import CCP4FileBrowser self.findProjectDialog = CCP4FileBrowser.CFileDialog(self,fileMode=QtGui.QFileDialog.Directory, title='Find project directory for '+projectId) self.connect(self.findProjectDialog, QtCore.SIGNAL('selectFile'), functools.partial(self.updateProjectDir,projectId)) self.findProjectDialog.show() def updateProjectDir(self,projectId,directory): if os.path.exists(directory) and os.path.exists(os.path.join(directory,'CCP4_DATABASE')): pass elif os.path.split(directory)[1] == 'CCP4_DATABASE': directory = os.path.split(directory)[0] else: mess = QtGui.QMessageBox.warning(self,'Find project directory for '+projectId,'The selected directory does not contain a CCP4_DATABASE sub-directory - it is not a valid CCP4 project directory.') return self.model().getProject(projectId).setDirectory(directory) err = self.model().getProject(projectId).editDatabaseDef(resetDir=True) err.append(self.model().editDirectoriesDefFile(projectDbIndex=self.model().getProject(projectId).refDbIndex,directory=directory)) if err.maxSeverity()>SEVERITY_WARNING: mess = QtGui.QMessageBox.warning(self,'Find project directory for '+projectId,"Failed saving changed project directory to old CCP4 files") return def collectProject(self,projectId): rv = QtGui.QMessageBox.question(self,'Collect input files for '+str(projectId),"Copy input files to project 'IMPORTED_FILES' directory.\nPlease close old CCP4i before running this.",QtGui.QMessageBox.Yes|QtGui.QMessageBox.No) if rv != QtGui.QMessageBox.Yes: return pObj = self.model().getProject(projectId) err = CErrorReport() import shutil importDir = os.path.join(pObj.directory,'IMPORTED_FILES') if not os.path.exists(importDir): try: os.mkdir(importDir) except: QtGui.QMessageBox.warning(self,'Failed creating IMPORTED_FILES directory','Do you have write permission for '+str(pObj.directory)) return elif not os.access(importDir , os.W_OK|os.X_OK): QtGui.QMessageBox.warning(self,'Can not write to IMPORTED_FILES directory','You do not have write permission for '+str(pObj.directory)) return dbFile = os.path.normpath(os.path.join(pObj.directory,'CCP4_DATABASE','database.def')) metaData,params,err = readI1DefFile(dbFile) if err.maxSeverity()>SEVERITY_WARNING or params.get('NJOBS',None) is None: return sourceLookup = {} movedFiles = {} nJobs =int( params['NJOBS'][1] ) for nJ in range(1,nJobs+1): fList = params['INPUT_FILES'][1][nJ] dList = params['INPUT_FILES_DIR'][1][nJ] #print 'collectProject',nJ,dList,fList if fList is not None and dList is not None: fList = fList.split() dList = dList.split() for nF in range(min(len(fList),len(dList))): f = fList[nF].strip('"') d = dList[nF].strip('"') #print 'collectProject',nF,d,f if d == 'FULL_PATH': #print 'collectProject to copy',nJ,nF,f if os.path.exists(f): if sourceLookup.has_key(os.path.split(f)[1]): #Already inported file of same name if sourceLookup[os.path.split(f)[1]] == f: target = os.path.join(importDir,os.path.split(f)[1]) else: # Same file name but from different source?? base,ext0 = os.path.split(f)[1].splitext() base,ext1 = os.path.split(base)[1].splitext() idx = 1 while sourceLookup.has_key( base+'_' + ('00'+str(idx))[-2:] + ext1 + ext0 ): idx += 1 filn= base+'_' + ('00'+str(idx))[-2:] + ext1 + ext0 #print 'collectProject incr target file',idx,filn target = os.path.join(importDir,filn) shutil.copyfile(f,target) sourceLookup[filn] = f else: target = os.path.join(importDir,os.path.split(f)[1]) #print 'copying to',target shutil.copyfile(f,target) sourceLookup[os.path.split(f)[1]] = f fList[nF] = target movedFiles[str(nJ)] = fList err.append(self.__class__,122,f+' input to job '+str(nJ)) else: err.append(self.__class__,121,f+' input to job '+str(nJ)) #print 'collectProject movedFiles' #for key,value in movedFiles.items(): print key,value if len(movedFiles)>0: err0 = pObj.editDatabaseDef(movedFiles=movedFiles) if err0.maxSeverity()>SEVERITY_WARNING: err0.warningMessage('Collect input files','There was an error saving the new filenames to the old CCP4 database',parent=self) self.reloadProject(projectId) def reloadProject(self,projectId): pObj = self.model().getProject(projectId) self.model().beginResetModel() pObj.loadDatabase() self.model().endResetModel() def annotateProject(self,projectId): #print 'annotateProject',projectId import CCP4Annotation,CCP4DataManager pObj = self.model().getProject(projectId) if pObj is None: return if getattr(self,'annotateWindow',None) is None: self.annotateWindow = QtGui.QDialog(self) self.annotateWindow.setLayout(QtGui.QVBoxLayout()) self.tagList = CCP4Annotation.CMetaDataTagList(parent=self, subItem_enumeratorsFunction = CI1PREFERENCES().getTagList, subItem_addEnumeratorFunction = CI1PREFERENCES().addTag ) # Beware CList.set() rebuilds list so must do this before creating the widget #print 'annotateProject tagList',self.tagList.__dict__['_value'] self.annotation = CCP4Annotation.CAnnotation(parent=self) self.tagListWidget = CCP4DataManager.DATAMANAGER().widget(model=self.tagList,parentWidget=self.annotateWindow, qualifiers= { 'listVisible':True, 'title' : 'Choose keyword tags for project - new tags can be entered' }) self.annotateWindow.layout().addWidget(self.tagListWidget) self.annotationWidget = CCP4DataManager.DATAMANAGER().widget(model=self.annotation,parentWidget=self.annotateWindow, qualifiers= { 'multiLine' : True, 'title' : 'Enter description of project' }) self.annotateWindow.layout().addWidget(self.annotationWidget) bb = QtGui.QDialogButtonBox(self.annotateWindow) but = bb.addButton(QtGui.QDialogButtonBox.Save) self.connect(but,QtCore.SIGNAL('clicked()'),self.saveAnnotation) but = bb.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('clicked()'),self.annotateWindow.close) self.annotateWindow.layout().addWidget(bb) self.annotateWindow.setWindowTitle('Annotate CCP4i project: '+projectId) self.annotationProject = projectId if pObj.annotation is not None: self.annotation.set(str(pObj.annotation)) else: self.annotation.unSet() self.tagList.unSet() self.tagList.set(pObj.tagList) #print 'pObj.tagList',repr(pObj),pObj.refProject,pObj.tagList #print 'self.tagList set to',self.tagList self.annotationWidget.updateViewFromModel() self.tagListWidget.updateViewFromModel() self.tagListWidget.handleRowChange(row=0,force=True) self.annotateWindow.show() self.annotateWindow.raise_() def makeI2Project(self,projectId): try: pid = CCP4Modules.PROJECTSMANAGER().db().getProjectId(projectName=projectId) except: reqNewName = False else: reqNewName = True import CCP4ProjectManagerGui if CCP4ProjectManagerGui.CNewProjectGui0.insts is None: CCP4ProjectManagerGui.CNewProjectGui0.insts = CCP4ProjectManagerGui.CNewProjectGui0() self.connect(CCP4ProjectManagerGui.CNewProjectGui0.insts,QtCore.SIGNAL('projectCreated'),functools.partial(self.associateI2Project,projectId)) else: CCP4ProjectManagerGui.CNewProjectGui0.insts.clear() CCP4ProjectManagerGui.CNewProjectGui0.insts.name.set(projectId) CCP4ProjectManagerGui.CNewProjectGui0.insts.show() def handleAssociateI2Project(self,i1ProjectName): #print 'handleAssociateI2Project',i1ProjectName import CCP4ProjectManagerGui self.associateDialog = QtGui.QDialog(self) self.associateDialog.setLayout(QtGui.QVBoxLayout()) self.associateDialog.layout().addWidget(QtGui.QLabel('Choose CCP4i2 project to associate with project '+i1ProjectName,self.associateDialog)) self.i2Selector = CCP4ProjectManagerGui.CProjectsTreeWidget(self) self.i2Selector.populate() self.associateDialog.layout().addWidget(self.i2Selector) butBox = QtGui.QDialogButtonBox(self.associateDialog) b = butBox.addButton(QtGui.QDialogButtonBox.Ok) self.connect(b,QtCore.SIGNAL('clicked()'),functools.partial(self.doAssociateDialog,i1ProjectName)) b = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(b,QtCore.SIGNAL('clicked()'),self.closeAssociateDialog) self.associateDialog.layout().addWidget(butBox) self.associateDialog.show() self.associateDialog.raise_() def closeAssociateDialog(self): self.associateDialog.close() self.associateDialog.deleteLater() del self.associateDialog def doAssociateDialog(self,i1ProjectName): projectId = self.i2Selector.selectedProjectId() if projectId is None: return self.associateI2Project(i1ProjectName,projectId) self.closeAssociateDialog() def associateI2Project(self,i1ProjectName,projectId=None): #print 'associateI2Project',i1ProjectName,projectId pObj = self.model().getProject(i1ProjectName) PROJECTSMANAGER().db().updateProject(projectId,key='I1ProjectName',value=i1ProjectName) PROJECTSMANAGER().db().updateProject(projectId,key='I1ProjectDirectory',value=pObj.directory) def saveAnnotation(self): #print 'saveAnnotation',self.tagList,self.annotation self.annotateWindow.close() pObj = self.model().getProject(self.annotationProject) pObj.tagList = [] for item in self.tagList: if item.tag.isSet(): pObj.tagList.append(str(item.tag)) pObj.annotation = str(self.annotation.text) # Remove any tags that were created but not ultimately used for this pObj CI1PREFERENCES().pruneTags( pObj.tagList ) #print 'saveAnnotation object',repr(pObj),pObj.refProject,pObj.tagList,pObj.annotation def getActionDef(self,name,**info): return self.actionDefinitions.get(name,dict(text=name)) def loadDb(self,overwriteProject=None): import CCP4FileBrowser dialog = CCP4FileBrowser.CFileDialog(self, title='Load old CCP4 project(s) from directories.def or database.def', filters= [ 'Old CCP4 directory.def or database.def (*.def)' ], defaultSuffix='.def', fileMode=QtGui.QFileDialog.ExistingFile ) dialog.widget.fileDialog.setFilter(QtCore.QDir.AllEntries | QtCore.QDir.Hidden | QtCore.QDir.NoDotAndDotDot ) dialog.show() def showPreferences(self): pass def createFolder(self,text=''): if getattr(self,'folderWidget',None) is None: d = QtGui.QDialog(self) d.setWindowTitle('I1 Projects Folder') d.setModal(True) d.setLayout(QtGui.QVBoxLayout()) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Enter unique name for folder',self)) self.folderWidget = QtGui.QLineEdit(self) line.addWidget(self.folderWidget) d.layout().addLayout(line) bb = QtGui.QDialogButtonBox(d) b = bb.addButton(QtGui.QDialogButtonBox.Ok) b.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(b,QtCore.SIGNAL('clicked()'),self.handleCreateFolder) b = bb.addButton(QtGui.QDialogButtonBox.Cancel) b.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(b,QtCore.SIGNAL('clicked()'),d.hide) bb.setCenterButtons(True) d.layout().addWidget(bb) self.folderWidget.setText(text) self.folderWidget.window().show() def handleCreateFolder(self): #print 'handleCreateFolder',self.folderWidget.text() name = str(self.folderWidget.text()) indx = self.model().getProject(name) if indx is not None: QtGui.QMessageBox.warning(self,'Create folder','The folder name is not unique') return self.folderWidget.window().hide() try: self.model().addFolder(name) except CException as e: e.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Failed to create new folder') def copyFile(self,fileName): #print 'CI1ProjectViewer.copyFile',fileName if os.path.exists(fileName): urlList = [QtCore.QUrl()] urlList[0].setPath(fileName ) mimeData = QtCore.QMimeData() mimeData.setUrls(urlList) QTAPPLICATION().clipboard().setMimeData(mimeData) def close(self): self.Exit() CCP4WebBrowser.CMainWindow.close(self) def Exit(self): self.model().saveStatus() def handleSave(self): pass def isProjectViewer(self): return False def widgetIsSaveable(self): return False def widgetIsPrintable(self): return False def widgetIsRunable(self): return False def widgetIsSearchable(self): return False def handlePrint(self): pass def handleRun(self): pass def openFind(self): pass def isFindFrameOpen(self): pass def deleteTab(self): pass def historyBack(self): pass def historyForward(self): pass def reloadPage(self): pass def openManageImportFiles(self): pass def openApplication(self): pass def openSendReport(self): pass def openUpdate(self): pass @staticmethod def updateInstances(qobj): l = [] for w in CI1ProjectViewer.Instances: if isAlive(w): l.append(w) CI1ProjectViewer.Instances = l # Put preferences in a class - could be subclassed from CContainer if # requirements expand to warrant that class CI1Preferences: insts = None def __init__(self): CI1Preferences.insts = self self.tagList = [] self.sources = [] self.load() self.appendedTags = [] def save(self,fileName=None): if fileName is None: fileName = os.path.join(CCP4Utils.getDotDirectory(),'i1supplement','preferences.xml') #print 'CI1Preferences.save',fileName f = CCP4File.CI2XmlDataFile(fullPath=fileName) if not os.path.exists(fileName): f.header.setCurrent() f.header.function='UNKNOWN' f.header.comment = 'Preferences for I1 Project Viewer' body = etree.Element('body') fEleList = etree.Element('tagList') body.append(fEleList) #print 'CI1Preferences.save tagList',self.tagList for tag in self.tagList: if tag is not None: fEleList.append(etree.Element('tag')) fEleList[-1].text = tag f.saveFile(bodyEtree=body) def load(self,fileName=None): if fileName is None: fileName = os.path.join(CCP4Utils.getDotDirectory(),'i1supplement','preferences.xml') if os.path.exists(fileName): f = CCP4File.CI2XmlDataFile(fullPath=fileName) root = f.getBodyEtree() if root.find('tagList') is not None: for fEle in root.find('tagList').findall('tag'): self.tagList.append(str(fEle.text)) def getTagList(self): return self.tagList def addTag(self,tag): if tag is None or len(tag)==0: return if self.tagList.count(tag): return self.tagList.index(tag) self.tagList.append(tag) self.appendedTags.append(tag) self.tagList.sort() return self.tagList.index(tag) def pruneTags(self,usedTags): # Remove any tags not eventually used in the annotation edit window for item in self.appendedTags: if item not in usedTags: if item in self.tagList: self.tagList.remove(item) self.appendedTags = []