""" CCP4DefEd.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. """ """ Liz Potterton Oct 2010 Create widget to edit def.xml files """ HELPTEXT = { 'Introduction' : """
This GUI is intended to help developers create a wrapper. Each CCP4i2 wrapper must have a DEF XML file which defines the input and output data and any control parameters. The main window will help create a DEF XML file. There are also options under the Tools
The first step is to create a wrapper directory using the Tools->Make wrapper plugin option. In the new window you must enter a unique name for the wrapper and choose whether it goes in the general wrappers or a particular pipeline directory. You have a choice of basic templates and of license to prepend to the template. You can add alternative licenses to the directory $CCP4I2/data/plugin_templates/license_templates.
Once the wrapper directory is created you should use the main defEd editor to create the DEF XML file. But, beware, if you are lucky there may already be COM template file for generating the input command file for the program that you which to run. If you want to use an existing COM template file then you must ensure that you parameter names are consistent with those used in the COM file. The DEF file should be saved with the path $CCP4I2/wrappers/myWrapper/script/myWrapper.def.xml.
The main functionality of this defEd editor is to create and edit DEF XML files for CCP4i2 GUI and pipelines. The editor is split vertically into three panes. The top pane shows the current contents tree, the middle pane is the object editor with tools to edit the currently selected item in the data definition and the bottom pane has help text. A right mouse click in any widget in the gui gives a menu with the option of help.
The current data contents tree is shown in the top pane along with buttons to..
Append container - a container is added at end of the 'top level' of the tree
Insert container - a container is inserted after current item at same level in the tree
Add object - add a data object in either the currently selected container or after the currently selected data object
Delete object - delete the currently selected container or data object
Click on any item in the tree to make it editable in the object editor.
You should start by appending a container and using the Name widget in the object editor to set a suitable name. The recommended def file structure (open for discussion) is to have three containers for inputData, outputData and controlParameters. The controlParameters could (should?) have sub-containers for the different components of a pipeline. Any additional parameters needed for gui function should be in a separate guiControls container.
When you add an object it is created with type CString. Use the table on the left side of the object editor to select the appropriate data class. A right mouse click on any item in this table will give documentation in the help pane. Also edit the Name for the data object. On the right side of the pane the appropriate qualifiers for each data class are shown. These enable simple customisations of the basic class so avoiding the need to write a Python subclass for trivial data.
The qualifiers are described in the class documentation but some general points:
The top line of the object editor enables defining a list or dictionary of the chosen data type. Note that, unlike Python list or dictionary a CList or CDict is of only one data type. A CListist has three qualifiers: listMinLength, listMaxLength and compare. Compare should be set to 1 or -1 to switch on validity checking that successive values in the list are ascending or descending in value. Note that the list item class must have a __cmp__() method.
The class to handle program input of MTZ columns, CProgramColumnGroup, is a special case for which the plugin developer can specify the contents of the class in the XML file. (It would be conceptually easier to insist on sub-classing!). The 'qualifiers' pane for this class includes a table in which you should enter the 'program' name of the column and the required column type. You can enter a comma-separated list of preferred column names, to be used by default if they appear in the MTZ file. The additional columns for Partner and Offset can probably be ignored.
The Save option on the File menu can be used to save the contents tree to an XML file. You will get an additional dialog box requiring the header information for the XML file.
Beware that defEd currently has no tools for Undo/Redo or moving data items. These are not an immediate priority (c.f. more data classes, command templates etc.) - please let me know if you find this a serious problem.
""", 'header' : """The header info for the DEF xml file. You only need to set the name (a single word) and version (in form n.m.i) for the plugin. Optionally add short title that will be used in the GUI.
""" } CONTAINER_NAMES = ['inputData','controlParameters','outputData','guiControls'] import os from PyQt4 import QtGui, QtCore from CCP4ErrorHandling import * from CCP4Config import DEVELOPER from CCP4DataManager import DATAMANAGER import CCP4Widgets,CCP4Data class CDefEd(QtGui.QMainWindow): insts = None MARGIN = 2 ERROR_CODES = { 101 : { 'description' : 'Unknown error reading def.xml file' }, 102 : { 'description' : 'Unknown error displaying contents of def.xml file' }, 103 : { 'description' : 'No data to save' }, 104 : { 'description' : 'Unknown error trying to set qualifier'}, 105 : { 'description' : 'Error attempting to create new object of class' }, 106 : { 'description' : 'Attempting to change name of data object to name of existing object:' }, 107 : { 'description' : 'Error finding container for new object' }, 108 : { 'description' : 'Error creating new CString object' }, 109 : { 'description' : 'Unknown error adding new object to data container:' }, 110 : { 'description' : 'Need to select or create object' }, 111 : { 'description' : 'Error making backup copy of current file' }, 112 : { 'description' : 'Unknown error saving to file' } } def __init__(self,parent=None): if not parent: try: import CCP4Modules parent = CCP4Modules.MAINWINDOW() except: pass QtGui.QMainWindow.__init__(self,parent) self.setWindowTitle('CCP4i2 defEd - DEF file editor') if not CDefEd.insts: CDefEd.insts = self self.openFileDialog = None self.saveFileDialog = None self.headerDialog = None self.container = None self.edited = False self.helpDocument = {} self.initialiseSave() self.newNameCount = 0 self.nameClashWarned = [] self.infoHistory = [] self.infoHistoryPosition = -1 self.loadedFileName = None self.menuDefinitions = {} self.menuDefinitions['File'] = ['open','save','saveAs','print','help','close'] self.menuDefinitions['Tools'] = ['makeWrapper','makePipeline','makeDataXml'] self.contextMenu = QtGui.QMenu(self) import functools for menuName,menuTitle in [['File','&File'],['Tools','&Tools']]: m = self.menuBar().addMenu(menuTitle) m.setObjectName(menuName) self.connect(m,QtCore.SIGNAL("aboutToShow()"),functools.partial(self.updateMenu,menuName)) self.initialiseActionDefinitions() mainWidget = QtGui.QSplitter(QtCore.Qt.Vertical,self) topFrame = QtGui.QFrame() topFrame.setLayout(QtGui.QGridLayout()) self.contentsTree = CContentsTree(self) self.contentsTree.setToolTip('The current data definition.') topFrame.layout().addWidget(self.contentsTree,0,0) self.buttonLayout = QtGui.QVBoxLayout() topFrame.layout().addLayout(self.buttonLayout,0,1) mainWidget.addWidget(topFrame) #self.connect(self.contentsTree,QtCore.SIGNAL('currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)'),self.handleCurrentItemChanged) self.connect(self.contentsTree,QtCore.SIGNAL('itemSelectionChanged()'),self.handleSelectionChanged) # Create buttons import functools self.appendContainerButton = CCP4Widgets.CPushButton(self,text='Append container') self.buttonLayout.addWidget(self.appendContainerButton) self.connect(self.appendContainerButton,QtCore.SIGNAL('clicked()'),functools.partial(self.handleNewContainer,True)) self.insertContainerButton = CCP4Widgets.CPushButton(self,text='Insert container') self.buttonLayout.addWidget(self.insertContainerButton) self.connect(self.insertContainerButton,QtCore.SIGNAL('clicked()'),self.handleNewContainer) self.newButton = CCP4Widgets.CPushButton(self,text='Add object') self.buttonLayout.addWidget(self.newButton) self.connect(self.newButton,QtCore.SIGNAL('clicked()'),self.handleNew) self.deleteButton = CCP4Widgets.CPushButton(self,text='Delete object') self.buttonLayout.addWidget(self.deleteButton) self.connect(self.deleteButton,QtCore.SIGNAL('clicked()'),self.handleDelete) self.defEditor = CDefEditor(self) self.defEditor.setEditMode() mainWidget.addWidget(self.defEditor) self.connect(self.defEditor.classBrowser,QtCore.SIGNAL('leftMousePress'),self.handleClassBrowserClick) self.connect(self.defEditor.classBrowser,QtCore.SIGNAL('rightMousePress'),self.handleClassInfoRequest) self.connect(self.defEditor,QtCore.SIGNAL('qualifierEdited'),self.handleQualifierEdited) self.connect(self.defEditor,QtCore.SIGNAL('nameChanged'),self.handleNameEdit) self.connect(self.defEditor.collectionFrame.classCombo,QtCore.SIGNAL('currentIndexChanged(int)'),self.handleListStateChanged) self.connect(self.defEditor.collectionFrame,QtCore.SIGNAL('qualifierEdited'),self.handleCollectionQualifierEdited) self.connect(self.defEditor.columnGroupWidget,QtCore.SIGNAL('edited'),self.handleColumnGroupEdited) self.setCentralWidget(mainWidget) frame = QtGui.QFrame(self) frame.setFrameShape(frame.Box) frame.setLayout(QtGui.QVBoxLayout()) frame.layout().setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) frame.layout().setSpacing(CDefEd.MARGIN) self.classInfoToolBar = QtGui.QToolBar('Class Information',self) self.classInfoToolBar.setMaximumSize(9999,25) import CCP4GuiUtils CCP4GuiUtils.populateToolBar(self,toolBarWidget=self.classInfoToolBar,definition=['back','forward']) frame.layout().addWidget(self.classInfoToolBar) self.classInfoWidget = QtGui.QTextBrowser(self) self.classInfoWidget.setOpenLinks(False) self.connect(self.classInfoWidget,QtCore.SIGNAL('anchorClicked(const QUrl &)'),self.handleAnchorClicked) frame.layout().addWidget(self.classInfoWidget) mainWidget.addWidget(frame) self.connect(self.contentsTree,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Contents tree')) self.connect(self.defEditor.columnGroupWidget,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Column group')) self.connect(self.defEditor.collectionFrame,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Lists')) for w in [self.appendContainerButton,self.insertContainerButton,self.newButton,self.deleteButton]: self.connect(w,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Data objects')) self.connect(self.defEditor.nameEditor,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Data names')) self.connect(self.defEditor.nameCombo,QtCore.SIGNAL('rightMousePress'),functools.partial(self.updateContextMenu,'Data names')) self.connect(self.defEditor,QtCore.SIGNAL('rightMousePress'),self.updateContextMenu) self.showHelp() self.show() if self.container is None: import CCP4Container self.container = CCP4Container.CContainer(name='NEWCONTAINER') QtCore.QTimer.singleShot(0,self.handleCommandLine) def handleCommandLine(self): import sys if len(sys.argv)>1: self.loadDefFile(sys.argv[1]) def updateMenu(self,menuName): widget = self.menuBar().findChild(QtGui.QMenu,menuName) if widget is None: pass else: widget.clear() import CCP4GuiUtils CCP4GuiUtils.populateMenu(self,widget,self.menuDefinitions[menuName],default_icon='') def initialiseActionDefinitions(self): self.actionDefinitions = {} self.actionDefinitions['close'] = dict ( text = 'Close editor', tip = 'Close this def file viewer', slot = self.close ) self.actionDefinitions['open'] = dict ( text = "Open file", tip = "Open def.xml file", slot = self.handleOpenFile, icon = 'fileopen', shortcut = self.tr('cmd+O') ) self.actionDefinitions['print'] = dict ( text = "Print", tip = "Print list of data objects", slot = self.handlePrint, shortcut = self.tr('cmd+P') ) def e(): return self.edited and (self.loadedFileName is not None) self.actionDefinitions['save'] = dict ( text = "Save", tip = "Backup current def.xml and save to same file name", slot = self.handleSaveFile, enabled = e ) def e(): return self.edited self.actionDefinitions['saveAs'] = dict ( text = "Save as", tip = "Save to def.xml file", slot = self.handleSaveAsFile, enabled = e ) self.actionDefinitions['help'] = dict ( text = "Help", tip = "Show main help", slot = self.showHelp ) self.actionDefinitions['makeWrapper'] = dict ( text = "Make wrapper plugin", tip = "Create directories and template files", slot = self.makeWrapper ) self.actionDefinitions['makePipeline'] = dict ( text = "Make pipeline project", tip = "Create directories and template files for a pipeline", slot = self.makePipeline, enabled = 1 ) self.actionDefinitions['makeDataXml'] = dict ( text = "Make data XML file", tip = "Specify data for testing pipeline/wrapper", slot = self.makeDataXml, enabled = 0 ) ''' self.actionDefinitions['makeHtml'] = dict ( text = "Make Html class listing", tip = "Make Html class listing", slot = self.makeHtmlClassListing, enabled = 0 ) ''' self.actionDefinitions['back'] = dict ( text = "Back", tip = "Go back to previous page", slot = self.handleBack, enabled = 0 ) self.actionDefinitions['forward'] = dict ( text = "Forward", tip = "Go to next page", slot = self.handleForward, enabled = 0 ) ''' self.actionDefinitions['find'] = dict ( text = "Find", tip = "Find in file", slot = self.openFind, icon = 'search', checkable = 1, checked = self.isFindFrameOpen, ) self.actionDefinitions['reload'] = dict ( text = "Reload", tip = "Reload current page", slot = self.reloadPage, enabled = 1 ) ''' def getActionDef(self,name,**info): return self.actionDefinitions.get(name,dict(text=name)) def updateContextMenu(self,mode=None,event=None): import functools if event is None or mode is None: return self.contextMenu.clear() a = self.contextMenu.addAction('Help: '+mode) self.connect(a,QtCore.SIGNAL('triggered(bool)'),functools.partial(self.updateHelp,mode)) self.contextMenu.popup(QtCore.QPoint(event.globalX(),event.globalY())) def handleOpenFile(self): rv = self.inviteSaveFile() if rv: return fileName = QtGui.QFileDialog.getOpenFileName(self, "Open def file", '', "Def files (*.def.xml)") if fileName is not None: self.loadDefFile(str(fileName)) def handlePrint(self): fileName = QtGui.QFileDialog.getSaveFileName(self, "Save to text file", '', "Text files (*.txt)") if fileName is not None: text = '' root = self.contentsTree.topLevelItem(0) for indx in range(self.contentsTree.topLevelItemCount()): t = self.treeToText( self.contentsTree.topLevelItem(indx) ,0) text = text + t import CCP4Utils CCP4Utils.saveFile(fileName=fileName,text=text) def treeToText(self,parent,level): d = [] for i in range(3): try: v = parent.data(i,0) d.append(v.toString().__str__()) except: d.append('') text = '' for i in range(level): text = text + ' ' text = text + "%-25s %-25s %s\n" % (d[0],d[1],d[2]) level += 1 for indx in range(parent.childCount()): t = self.treeToText( parent.child(indx) ,level) text = text + t return text def loadDefFile(self,fileName): from CCP4Container import CContainer self.container = CContainer(parent=self) #Expect loadContentsFromXml to return the error but put in try/except # just in case #try e = self.container.loadContentsFromXml(fileName) ''' except CException as e: pass except: e = CException(self.__class__,101,fileName) ''' if len(e)>0: e.warningMessage() e = self.contentsTree.populate(container=self.container) if len(e)>0: e.warningMessage('Error drawing contents tree for def file') self.initialiseSave() if self.contentsTree.firstDataItem is None: self.defEditor.unSet() else: self.contentsTree.setCurrentItem(self.contentsTree.firstDataItem) self.contentsTree.scrollToItem(self.contentsTree.firstDataItem) ''' firstDataObject = self.container.firstDataObject() if firstDataObject is not None: treeItemList = self.contentsTree.findItems(str(firstDataObject.objectName()),QtCore.Qt.MatchExactly) if len(treeItemList)>0: self.contentsTree.setCurrentItem(treeItemList[0]) self.contentsTree.scrollToItem(treeItemList[0]) ''' self.loadedFileName = fileName self.setWindowTitle('CCP4i2 defEd: '+os.path.basename(fileName)) self.edited = False def handleSaveFile(self): import shutil if self.loadedFileName is None: return if os.path.exists(self.loadedFileName): bak = 1 bakFile = self.loadedFileName + '.'+str(bak)+'.bak' while os.path.exists(bakFile): bak = bak+1 bakFile = self.loadedFileName + '.'+str(bak)+'.bak' try: shutil.copyfile(self.loadedFileName,bakFile) except: e = CException(self.__class__,111,bakFile) e.warningMessage() #print 'CDefEd.handleSaveFile',self.loadedFileName try: self.container.saveContentsToXml(self.loadedFileName) except CException as e: e.warningMessage() except Exception as e: e = CException(self.__class__,112,self.loadedFileName) e.warningMessage() self.edited = False def handleSaveAsFile(self): import CCP4Utils if not self.edited: QtGui.QMessageBox.warning(self,'No data to save','No changes to data to save') return fileName = QtGui.QFileDialog.getSaveFileName (self, 'Save def file',CCP4Utils.getCCP4I2Dir() , "Def files (*.xml)" ) if len(fileName) ==0: return base,ext0 = os.path.splitext(str(fileName)) ext1 = os.path.splitext(base)[1] #print 'handleSaveAsFile',base,ext0,'*',ext1 if len(ext1) == 0 and ext0 == '.xml': fileName = base + '.def' + '.xml' self.saveDef(fileName) ''' if self.saveFileDialog is None: self.saveFileDialog = QtGui.QFileDialog(self,"Save def file", '', "Def files (*.def.xml)") self.saveFileDialog.setDefaultSuffix('def.xml') self.saveFileDialog.setDirectory(CCP4Utils.getCCP4I2Dir()) self.saveFileDialog.setConfirmOverwrite(False) self.saveFileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.saveFileDialog.setModal(True) self.connect(self.saveFileDialog, QtCore.SIGNAL('filesSelected(const QStringList&)'),self.saveDef) self.saveFileDialog.exec_() ''' def initialiseHeader(self,pluginName=''): if self.container is None: return h = self.container.addHeader() h.function='DEF' h.creationTime.setCurrentTime() h.userId.setCurrentUser() h.pluginName = pluginName from CCP4Config import VERSION h.ccp4iVersion = VERSION() def saveDef(self,fileName): #print 'CDefEd.saveData',fileName basename = os.path.splitext(fileName)[0] if isinstance(fileName,QtCore.QStringList): if len(fileName) == 0: return fileName = str(fileName[0]) if self.container is None: e = CException(self.__class__,103,fileName) e.warningMessage() return if str(self.container.objectName()) == 'NEWCONTAINER': self.container.setObjectName(basename) self.setWindowTitle('CCP4i2 defEd: '+basename) self.showHelp('header') self.initialiseHeader(pluginName=basename) # Recreate headerDialog every time so the call to saveDef2 uses the right fileName self.headerDialog = CHeaderDialog(self,model=self.container.header) import functools self.connect(self.headerDialog,QtCore.SIGNAL('closed'),functools.partial(self.saveDef2,fileName)) done = self.headerDialog.show() def saveDef2(self,fileName): #try: self.container.saveContentsToXml(fileName) #except CException as e: # e.warningMessage() #except Exception as e: # e = CException(self.__class__,112,self.loadedFileName) # e.warningMessage() self.edited = False self.loadedFileName = fileName def handleSelectionChanged(self): from CCP4Container import CContainer from CCP4XtalData import CProgramColumnGroup current = self.contentsTree.selectedItem() #print 'handleSelectionChanged',current dataObj = self.dataObjectFromTreeWidgetItem(current) if dataObj is None: pass ifContainer = isinstance(dataObj,CContainer) #print 'handleCurrentItemChanged Selected CContainer' self.defEditor.set(dataObj) self.defEditor.setEditMode(ifContainer) self.defEditor.setColumnGroupWidget(dataObj) self.nameClashWarned = [] def currentDataObject(self): treeItem = self.contentsTree.selectedItem() if treeItem is None: return None dataObj = self.dataObjectFromTreeWidgetItem(treeItem) #print 'currentDataObject',treeItem,repr(dataObj) return dataObj def dataObjectFromTreeWidgetItem(self,item): nameList = [] while isinstance(item,QtGui.QTreeWidgetItem): nameList.insert(0,str(item.text(0))) item = item.parent() #print 'dataObjectFromTreeWidgetItem',nameList obj = self.container try: for name in nameList: obj = obj.get(name) except: return None #print 'dataObjectFromTreeWidgetItem',repr(obj),obj.__class__ return obj def redrawCurrentItem(self): # Update the text of the current item in the content browser dataObject = self.currentDataObject() if dataObject is None: return currentItem = self.contentsTree.currentItem() if currentItem is None: return self.drawContentTreeItem(currentItem,dataObject) def drawContentTreeItem(self,item,dataObject): import types import CCP4XtalData item.setText(0,str(dataObject.objectName())) item.setText(1,dataObject.__class__.__name__) qText = '' qualis = dataObject.qualifiers(default=False) for key,value in qualis.items(): if value is None or value is NotImplemented: pass elif isinstance(value,types.ListType) and len(value)>0 and isinstance(value[0],CCP4Data.CData): # This is to deal withthe CProgramColumnGroup columnGroups qualifiers l = [] for obj in value: l.append(str(obj.get())) qText = str(l) elif isinstance(value,CCP4Data.CData): qText = qText+key+':'+str(value.get()) else: try: qText = qText+key+':'+str(value)+ ' ' except: pass if isinstance(dataObject,CCP4Data.CCollection): qText = qText + 'subItem:'+dataObject.subItemClassName()+' ' qualis = dataObject.subItemQualifiers(default=False) for key,value in qualis.items(): if value is not None and value is not NotImplemented: try: qText = qText+key+':'+str(value)+ ' ' except: pass #print 'drawContentTreeItem',repr(dataObject),dataObject.objectName(),qualis,qText item.setText(2,qText) def handleDelete(self): dataObject = self.currentDataObject() if dataObject is None: return container = dataObject.parent() if container is not None: container.deleteObject(dataObject.objectName()) self.contentsTree.deleteCurrentItem() #print 'handleDelete',repr(container),container.contents().keys() self.edited= True def insertPositionInContainer(self): from CCP4Container import CContainer # Figure out which container and which afterObject dataObject = self.currentDataObject() if dataObject is None: #Put new item after last item at top level container = self.container afterObject = None elif isinstance(dataObject,CContainer): #put new item at end of container container = dataObject if len(container.dataOrder())>0: afterObject = container.dataOrder()[-1] else: afterObject = None else: #Assume is a CData in a container container = dataObject.parent() if container is None or not isinstance(container,CContainer): # Very odd! e = CException(self.__class__,107) e.warningMessage() return afterObject = str(dataObject.objectName()) #print 'handleNew',repr(container),repr(afterObject) return container,afterObject def newName(self,rootName): if self.newNameCount == 0: newName = rootName else: newName = rootName+'_'+str(self.newNameCount) self.newNameCount = self.newNameCount + 1 return newName def handleNew(self): from CCP4Container import CContainer from CCP4Data import CString container,afterObject = self.insertPositionInContainer() # unique name newName = self.newName('NONAME') # Make a default CString object try: newObject = CString(name=newName,parent=container) except: e = CException(self.__class__,108) e.warningMessage() return #print 'newObject',repr(newObject) # Add object to container try: container.addObject(newObject,name=newName,afterObject=afterObject) except CException as e: e.warningMessage() return except: e = CException(self.__class__,109,str(container.objectName())) e.warningMessage() return # Add to contentsTree newItem = self.contentsTree.insertObject(name=newName,defn=container.contents(newName)) self.contentsTree.setCurrentItem(newItem) self.defEditor.classBrowser.setSelectedClass('CString') self.edited= True self.nameClashWarned = [] def handleNewContainer(self,append=False): from CCP4Container import CContainer if append: container = self.container afterObject = None else: container,afterObject = self.insertPositionInContainer() # unique name newName = self.newName('CONTAINER') # Make default object try: newObject = CContainer(name=newName,parent=container) except: e = CException(self.__class__,108) e.warningMessage() return #print 'newObject',repr(newObject) # Add object to container try: container.addObject(newObject,name=newName,afterObject=afterObject) except CException as e: e.warningMessage() return except: e = CException(self.__class__,109,str(container.objectName())) e.warningMessage() return # Add to contentsTree if append: newItem = self.contentsTree.appendObject(name=newName,defn=container.contents(newName)) else: newItem = self.contentsTree.insertObject(name=newName,defn=container.contents(newName)) self.defEditor.setNameComboDefault(newName) self.contentsTree.setCurrentItem(newItem) self.edited= True self.nameClashWarned = [] def handleClassInfoRequest(self,modelItem=None,className=None): #print 'handleClassInfoRequest',modelItem,modelItem.text() if className is None: className = str(modelItem.text()) self.setClassInfoDocument(className) self.infoHistory = [className] self.infoHistoryPosition = len(self.infoHistory)-1 self.setInfoHistoryActions() def setClassInfoDocument(self,className): classInfo = CClassInfo(className=className,parent=self) self.classInfoWidget.setDocument(classInfo) def handleAnchorClicked(self,url): className = (str(url.toString())).split('#')[-1] self.setClassInfoDocument(className=className) if self.infoHistoryPosition < len(self.infoHistory)-1: del self.infoHistory[self.infoHistoryPosition+1:] self.infoHistory.append(className) self.infoHistoryPosition = len(self.infoHistory)-1 self.setInfoHistoryActions() def handleBack(self): self.infoHistoryRestore(-1) def handleForward(self): self.infoHistoryRestore(1) def infoHistoryRestore(self,increment=0): if increment<=0: new=max(0,self.infoHistoryPosition+increment) else: new = min(len(self.infoHistory)-1,self.infoHistoryPosition+increment) if new != self.infoHistoryPosition: self.infoHistoryPosition = new self.setClassInfoDocument(self.infoHistory[self.infoHistoryPosition]) self.setInfoHistoryActions() def updateHelp(self,mode=None): #print 'updateHelp',mode if HELPTEXT.has_key(mode): self.showHelp(mode) elif self.helpDocument.get('Introduction',None) is not None and self.helpDocument['Introduction'].find('name="'+mode+'"'): self.showHelp('Introduction') self.classInfoWidget.scrollToAnchor(mode) else: cls = DATAMANAGER().getClass(mode) if cls is not None: self.handleClassInfoRequest(className=mode) def showHelp(self,name=None): if name is None: name = 'Introduction' if self.helpDocument.get(name,None) is None: self.helpDocument[name] = QtGui.QTextDocument(self) self.helpDocument[name].setHtml(HELPTEXT[name]) self.infoHistory = [] self.infoHistoryPosition = -1 self.classInfoWidget.setDocument(self.helpDocument[name]) self.setInfoHistoryActions() def setInfoHistoryActions(self): nS = len(self.infoHistory) nP = self.infoHistoryPosition self.findChild(QtGui.QAction,'forward').setEnabled(nS>0 and nP<(nS-1)) self.findChild(QtGui.QAction,'back').setEnabled(nS>0 and nP>0) def close(self): if self.edited: rv = self.inviteSaveFile() if rv: return QtGui.QMainWindow.close(self) def initialiseSave(self): self.nAutoSaved = 0 self.autoSavedStatus = [] self.autoSavedPosition = -1 def autoSave(self): self.nAutoSaved = self.nAutoSaved + 1 self.autoSavedStatus.append(self.getSaveStatus()) self.autoSavedPosition = len(self.autoSavedStatus)-1 def autoRestore(self,increment=0): if increment<=0: new=max(0,self.autoSavedPosition+increment) else: new = min(len(self.autoSavedStatus)-1,self.autoSavedPosition+increment) if new != self.autoSavedPosition: self.autoSavedPosition = new self.updateStatus(status=self.autoSavedStatus[self.autoSavedPosition]) def getSaveStatus(self): current = self.currentDataObject() if current is not None: currentName = str(current.objectName()) else: currentName = None return { 'container' : self.container.saveContentsToEtree(), 'current': currentName } def updateStatus(self,status={}): self.container.clear() self.container.loadContentsFromEtree(status['container']) if status['current'] is None: dataObj = None else: dataObj = self.container.get(status['current']) if dataObj is None: self.defEditor.unSet() else: self.defEditor.set(dataObj) def handleQualifierEdited(self,qualifierName): #print 'handleQualifierEdited',qualifierName textValue = self.defEditor.getQualifierValue(qualifierName) if textValue is None: print 'Error getting qualifier value from widget for',qualifierName return try: exec('value='+textValue) except: value = textValue #print 'handleQualifierEdited',textValue,value dataObject = self.currentDataObject() if dataObject is not None: if isinstance(dataObject,CCP4Data.CCollection): subObj = dataObject.subItemObject() if subObj is not None: try: subObj.setQualifier(qualifierName,value) except CException as e: e.warningMessage() except: e = CException(self.__class__,104,qualifierName) e.warningMessage() else: try: dataObject.setQualifier(qualifierName,value) except CException as e: e.warningMessage() except: e = CException(self.__class__,104,qualifierName) e.warningMessage() self.redrawCurrentItem() self.edited= True def handleCollectionQualifierEdited(self,qualifierName): value = self.defEditor.collectionFrame.getQualifiers().get(qualifierName) #print 'CDefEd.handleCollectionQualifierEdited',qualifierName,value,type(value) dataObject = self.currentDataObject() #print 'handleCollectionQualifierEdited dataObject',repr(dataObject) if dataObject is not None: #try: if 1: dataObject.setQualifier(qualifierName,value) ''' except CException as e: e.warningMessage() except: e = CException(self.__class__,104,qualifierName) e.warningMessage() ''' self.redrawCurrentItem() self.edited= True def handleClassBrowserClick(self,item): className = str(item.text()) if className == self.defEditor.currentClassName: return if self.defEditor.currentClassName == 'CContainer': return dataObject = self.currentDataObject() if dataObject is None: # We dont have any item selected for editing but let the user see # The qualifiers editor for the chosen class self.defEditor.changeDataClass(className) self.defEditor.setDefaultQualifiers() return name = str(dataObject.objectName()) newObject = self.makeNewObject(className,self.defEditor.collectionFrame.getContainerClassName(),dataObject) #print 'handleClassBrowserClick newObject',repr(newObject) container = dataObject.parent() container.replaceObject(newObject,name=name) self.defEditor.changeDataClass(className) self.defEditor.setQualifiers(newObject.qualifiers()) self.defEditor.setColumnGroupWidget(newObject) self.redrawCurrentItem() self.edited= True def handleListStateChanged(self,indx): # User has toggled the list button so must convert to/from a list dataObject = self.currentDataObject() if dataObject is None: e = CException(self.__class__,110) e.warningMessage() return #print 'handleListStateChanged', state,isinstance(dataObject,CList) if isinstance(dataObject,CCP4Data.CCollection): className = dataObject.subItemClassName() else: className = dataObject.__class__.__name__ newObject = self.makeNewObject(className,self.defEditor.collectionFrame.getContainerClassName(),dataObject) if newObject is None: return container = dataObject.parent() name = str(dataObject.objectName()) container.replaceObject(newObject,name=name) self.redrawCurrentItem() self.edited= True def makeNewObject(self,className=None,containerClassName=None,oldObject=None): newObject = None name = str(oldObject.objectName()) if containerClassName is None: try: newObject = DATAMANAGER().getClass(className)(name=name) except: e = CException(self.__class__,105,className) e.warningMessage() return if isinstance(oldObject,CCP4Data.CCollection) and oldObject.subItemClassName() == className: try: newObject.setQualifiers(oldObject.subItemQualifiers()) except: pass else: if isinstance(oldObject, DATAMANAGER().getClass(containerClassName)): #Change the item class in a list/dict if className == oldObject.subItemClassName(): return None oldObject.setSubItem( { 'class' : DATAMANAGER().getClass(className) , 'qualifiers' : {} } ) newObject = oldObject else: cls = DATAMANAGER().getClass(className) if isinstance(oldObject,cls): subItemQualifiers=oldObject.qualifiers(default=False) elif isinstance(oldObject,CCP4Data.CCollection) and oldObject.subItemClassName() == className: subItemQualifiers=oldObject.subItemQualifiers(default=False) else: subItemQualifiers = {} if ['CList','CDict','CTable'].count(containerClassName): qualifiers=self.defEditor.collectionFrame.getQualifiers() else: qualifiers = {} newObject = DATAMANAGER().getClass(containerClassName)(name=name,qualifiers=qualifiers,subItem={ 'class' : cls, 'qualifiers' : subItemQualifiers } ) #print 'makeNewObject',repr(newObject) return newObject def handleNameEdit(self): newName = self.defEditor.getName() dataObject = self.currentDataObject() if dataObject is None: return oldName = dataObject.objectName() if newName == oldName: return container = dataObject.parent() #print 'handleNameEdit',oldName,newName,container try: container.renameObject(oldName,newName) except CException as e: if not self.nameClashWarned.count(newName): self.nameClashWarned.append(newName) e.warningMessage() # Beware until the content tree is updated to reflect new name # there is descrepancy between gui and container and dataObjectFromTreeWidgetItem and # currentDataObject don't work self.drawContentTreeItem(self.contentsTree.selectedItem(),dataObject) self.defEditor.currentContentName = newName self.edited= True def handleColumnGroupEdited(self,iRow,iCol): data = self.defEditor.columnGroupWidget.get() dataObject = self.currentDataObject() #print 'handleColumnGroupEdited',data,dataObject.objectName() if dataObject is None: return dataObject.setColumnGroupQualifier(columnGroup=data,initialise=True) self.drawContentTreeItem(self.contentsTree.selectedItem(),dataObject) self.edited = True def inviteSaveFile(self): #print 'inviteSaveFile',self.edited if not self.edited: return ''' self.saveMessage = QtGui.QMessageBox.question(self) self.saveMessage.setWindowTitle('CCP4DefEd Save changes') self.saveMessage.setText('Save changes to file?') self.saveMessage.setStandardButtons(QtGui.QMessageBox.Save|QtGui.QMessageBox.Cancel) rv = self.saveMessage._exec() ''' rv = QtGui.QMessageBox.question(self,'CCP4DefEd Save changes','Save changes to file?', QtGui.QMessageBox.Save|QtGui.QMessageBox.Cancel|QtGui.QMessageBox.Discard, QtGui.QMessageBox.Cancel) if rv == QtGui.QMessageBox.Cancel: return 1 elif rv == QtGui.QMessageBox.Discard: return 0 else: self.handleSaveFile() return 0 def makeWrapper(self): if not hasattr(self,'wrapperDialog'): self.wrapperDialog = CMakeWrapperPlugin(self) self.wrapperDialog.show() def makePipeline(self): if not hasattr(self,'pipelineDialog'): self.pipelineDialog = CMakePipeline(self) self.pipelineDialog.show() def makeDataXml(self): pass class CContentsTree(QtGui.QTreeWidget): ERROR_CODES = { 101 : { 'description' : 'Failed to draw container item in tree' }, 102 : { 'description' : 'Failed to draw data item in tree' }, 103 : { 'description' : 'Failed to draw qualifiers for data item in tree' }, 104 : { 'description' : 'No currently selected item to delete' } } def __init__(self,parent): QtGui.QTreeWidget.__init__(self,parent) self.setColumnCount(3) self.setHeaderLabels(['Name','Class','Qualifiers']) self.setItemsExpandable(True) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.firstDataItem = None def sizeHint(self): return QtCore.QSize(400,200) def mousePressEvent(self,event): if event.button() == QtCore.Qt.RightButton: self.emit(QtCore.SIGNAL('rightMousePress'),event) event.accept() else: QtGui.QTreeWidget.mousePressEvent(self,event) def populate(self,container=None): e = CErrorReport() self.clear() if container is None: return #top = QtGui.QTreeWidgetItem([str(container.objectName())]) #self.addTopLevelItem(top) e.extend(self.loadContainer(container=container,parent=None)) return e def clear(self): self.firstDataItem = None QtGui.QTreeWidget.clear(self) def loadContainer(self,container=None,parent=None): import CCP4Container e = CErrorReport() for name in container.dataOrder(): defn = container.contents(name) if defn is not None: try: item = self.makeTreeWidgetItem(name,defn) except CException as err: e.extend(err) else: if parent is None: self.addTopLevelItem(item) else: parent.addChild(item) if defn['class'] == CCP4Container.CContainer: e.extend(self.loadContainer(container.__getattr__(name),item)) else: if self.firstDataItem is None: self.firstDataItem = item return e def makeTreeWidgetItem(self,name,defn): import CCP4Container,CCP4Data import types item = None if defn['class'] == CCP4Container.CContainer: item = QtGui.QTreeWidgetItem([name],1001) #item.setFlags(QtCore.Qt.ItemIsEnabled) item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled) else: qText = '' if isinstance(defn['class'],CCP4Data.CCollection): qText = 'subItem:'+defn['subItem']['class'].__name__ qualifiers = defn['subItemQualifiers'] else: qualifiers = defn['qualifiers'] try: for key,value in qualifiers.items(): if isinstance(value,types.ListType) and len(value)>0 and isinstance(value[0],CCP4Data.CData): # This is to deal withthe CProgramColumnGroup columnGroups qualifiers l = [] for item in value: l.append(str(item.get())) qText = str(l) elif isinstance(value,CCP4Data.CData): qText = qText+key+':'+str(value.get()) else: qText = qText+key+':'+str(value)+ ' ' except: raise CException(self.__class__,103,name) #print 'qualifiers',defn['qualifiers'],qText try: item = QtGui.QTreeWidgetItem([name,defn['class'].__name__,qText],1002) item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled) except: raise CException(self.__class__,102,name) return item def insertObject(self,name,defn): item = self.makeTreeWidgetItem(name,defn) currentItem = self.selectedItem() #print 'insertObject',name,currentItem, #if currentItem is not None: print int(currentItem.type()) if currentItem is None: self.addTopLevelItem(item) elif int(currentItem.type()) == 1001: currentItem.addChild(item) else: containerItem = currentItem.parent() #print 'insertObject parent',containerItem if containerItem is None: indx = self.indexOfTopLevelItem(currentItem) self.insertTopLevelItem(indx+1,item) else: indx = containerItem.indexOfChild(currentItem) #print 'insertObject indx',indx containerItem.insertChild(indx+1,item) return item def appendObject(self,name,defn): item = self.makeTreeWidgetItem(name,defn) self.addTopLevelItem(item) return item def deleteCurrentItem(self): currentItem = self.currentItem() if currentItem is None: raise CException(self.__class__,104) parent = currentItem.parent() if parent is None: # its a top level item indx = self.indexOfTopLevelItem(currentItem) nextIndx = min (indx, self.topLevelItemCount()-2) removed = self.takeTopLevelItem(indx) if nextIndx>=0: self.setCurrentItem(self.topLevelItem(nextIndx)) removed = None else: indx = parent.indexOfChild(currentItem) try: nextItem = parent.child(indx+1) except: nextItem = parent parent.removeChild(currentItem) self.setCurrentItem(nextItem) currentItem = None def selectedItem(self): seleList = self.selectedItems() #print 'contentsTree.selectedItem',seleList if len(seleList) == 0: return None elif len(seleList) == 1: return seleList[0] else: #print 'Error in CContentsTree - more than one selected item' return seleList[0] class CDataClassBrowser(QtGui.QTreeView): def __init__(self,parent): QtGui.QTreeView.__init__(self,parent) #self.setHeaderLabels(['Class','Description']) self.setItemsExpandable(True) self.setHeaderHidden(True) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) #self.connect(self,QtCore.SIGNAL('doubleCLicked(const QModelIndex &)'),self.doubleClicked) self.populate() def populate(self): model = DATAMANAGER().buildQStandardItemModel(parent=self) self.setModel(model) def selectedClass(self): selList = self.selectedItems() #print 'selectedClass',selList if len(selList) == 1: clsName = str(selList[0].text()) #print 'changeDataClass clsName',clsName return clsName else: return None def setSelectedClass(self,clsName=None): if clsName is None: return itemList = self.searchModelTree(clsName) #print 'CDataClassBrowser.setSelectedClass',clsName,type(clsName),itemList self.blockSignals(True) if len(itemList)==1: modelIndex = itemList[0].index() if modelIndex is not None: #print 'CDataClassBrowser.setSelectedClass modelIndex', modelIndex self.setCurrentIndex(modelIndex) self.blockSignals(False) def searchModelTree(self,text): # Surely there is method to search a model for specific text label??? # But I cant figure what is is so try this .. mrow = 0 hits = [] #print 'searchModelTree',self.model(),self.model().rowCount() while mrow < self.model().rowCount(): moduleItem = self.model().item(mrow,0) irow = 0 #print 'moduleItem',mrow,moduleItem,moduleItem.rowCount() while irow < moduleItem.rowCount(): clsItem = moduleItem.child(irow,0) #print 'clsItem',str(clsItem.text()) if str(clsItem.text()) == text: hits.append(clsItem) irow = irow + 1 mrow = mrow + 1 return hits def mousePressEvent(self,event): if [QtCore.Qt.RightButton,QtCore.Qt.LeftButton].count(event.button()): modelIndex = self.indexAt(event.pos()) if modelIndex is not None: model = modelIndex.model() if not model: QtGui.QTreeView.mousePressEvent(self,event) return item = model.itemFromIndex(modelIndex) #print 'mousePressEvent',modelIndex,model,item if item.rowCount()>0: #event.ignore() pass else: if event.button() == QtCore.Qt.RightButton: self.emit(QtCore.SIGNAL('rightMousePress'),item) else: self.emit(QtCore.SIGNAL('leftMousePress'),item) #print 'mousePressEvent calling QTreeView' QtGui.QTreeView.mousePressEvent(self,event) ''' def doubleClicked(self,modelIndex): print 'doubleClicked',modelIndex ''' class CDefEditor(QtGui.QFrame): ERROR_CODES = { 101 : { 'description' : 'Failed to retieve qualifier definition for class' }, 102 : { 'description' : 'Failed to draw qualifier definition widget for class' }, 103 : { 'description' : 'Failed to set default value qualifier' }, 104 : { 'description' : 'Error deleting qualifier widget' } } def __init__(self,parent=None): QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QVBoxLayout()) self.layout().setSpacing(CDefEd.MARGIN) self.layout().setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) self.setFrameShape(self.Box) self.currentClassName = None # The name line lineLayout = QtGui.QHBoxLayout() lineLayout.addWidget(QtGui.QLabel('Name:',self)) lineLayout.setSpacing(CDefEd.MARGIN) lineLayout.setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) self.nameStack = QtGui.QStackedWidget(self) self.nameEditor = CCP4Widgets.CLineEdit(self) self.nameStack.addWidget(self.nameEditor) # Beware - this will not work with CCp4Combo widget self.nameCombo= QtGui.QComboBox(self) self.nameCombo.addItem('CONTAINER') for item in CONTAINER_NAMES:self.nameCombo.addItem(item) self.nameCombo.setEditable(True) self.nameStack.addWidget(self.nameCombo) lineLayout.addWidget(self.nameStack) self.layout().addLayout(lineLayout) self.containerFrameStack = QtGui.QStackedWidget(self) self.collectionFrame = CCollectionFrame(self) self.containerFrameStack.addWidget(self.collectionFrame) dummyLine = QtGui.QFrame(self) dummyLine.setLayout(QtGui.QHBoxLayout()) dummyLine.layout().addWidget(QtGui.QLabel('The selected item is a container - you can edit the name and add other objects')) self.containerFrameStack.addWidget(dummyLine) self.layout().addWidget(self.containerFrameStack) self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal,self) self.layout().addWidget(self.splitter) self.layout().setStretch(1,10) leftFrame = QtGui.QFrame(self) leftFrame.setLayout(QtGui.QGridLayout()) leftFrame.layout().setSpacing(CDefEd.MARGIN) leftFrame.layout().setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) lineLayout = QtGui.QHBoxLayout() lineLayout.addWidget(QtGui.QLabel('Class:',self)) self.classLabel = QtGui.QLabel(self) lineLayout.addWidget(self.classLabel) lineLayout.addStretch(5) leftFrame.layout().addLayout(lineLayout,0,0) self.classBrowser = CDataClassBrowser(self) leftFrame.layout().addWidget(self.classBrowser,1,0) self.splitter.addWidget(leftFrame) self.rightFrame = QtGui.QFrame(self) self.rightFrame.setLayout(QtGui.QGridLayout()) self.rightFrame.layout().setSpacing(CDefEd.MARGIN) self.rightFrame.layout().setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) import functools self.connect(self.nameEditor,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('nameChanged'))) self.connect(self.nameCombo,QtCore.SIGNAL('editTextChanged()'),functools.partial(self.emit,QtCore.SIGNAL('nameChanged'))) self.connect(self.nameCombo,QtCore.SIGNAL('currentIndexChanged(int)'),functools.partial(self.emit,QtCore.SIGNAL('nameChanged'))) vLayout = QtGui.QVBoxLayout() self.rightFrame.layout().addLayout(vLayout,0,0) self.qFrame = QtGui.QFrame(self) self.qFrame.setLayout(QtGui.QGridLayout()) self.rightFrame.layout().addWidget(self.qFrame,1,0) self.columnGroupWidget = CProgramColumnGroupQualifierView(self) self.columnGroupWidget.setVisible(0) self.columnGroupWidget.blockSignals(True) self.rightFrame.layout().addWidget(self.columnGroupWidget,2,0) self.splitter.addWidget(self.rightFrame) self.splitter.setSizes([150,300]) def deleteQualiferWidget(self): #print 'deleteQualiferWidget',self.qFrame if self.qFrame is None: return self.splitter.widget(1).layout().removeWidget(self.qFrame) self.qFrame.deleteLater() self.qFrame = None def drawQualifiers(self,cls): # Not using CCP4Widgets 'cos want to keep the standard context menu with paste function import types,functools try: obj = cls() qDefs = obj.qualifiersDefinition() qOrder = obj.qualifiersOrder() except: e = CException(self.__class__,101,cls.__name__) e.warningMessage() return self.deleteQualiferWidget() self.qFrame = QtGui.QFrame(self) self.qFrame.setLayout(QtGui.QGridLayout()) self.rightFrame.layout().addWidget(self.qFrame,1,0) e = CErrorReport() iRow = -1 for quali in qOrder: defn = qDefs.get(quali,{}) #print 'drawQualifiers',quali,defn try: widget = None qType = defn.get('type',None) editable = defn.get('editable',True) if editable: if qType is None: qType = obj.pythonType() if qType == types.StringType: #widget = CCP4Widgets.CLineEdit(self.qFrame) widget = QtGui.QLineEdit(self.qFrame) elif qType == types.IntType: #widget = CCP4Widgets.CLineEdit(self.qFrame) widget = QtGui.QLineEdit(self.qFrame) validator = QtGui.QIntValidator(self.qFrame) widget.setValidator(validator) elif qType == types.FloatType: #widget = CCP4Widgets.CLineEdit(self.qFrame) widget = QtGui.QLineEdit(self.qFrame) validator = QtGui.QDoubleValidator(self.qFrame) widget.setValidator(validator) elif qType == types.BooleanType: widget = CCP4Widgets.CCheckBox(self.qFrame) if quali == 'default' and qDefs.get('allowUndefined',True): widget.setTristate() elif qType == 'enumerator': menu = defn.get('menu',[]) widget = CCP4Widgets.CComboBox(self.qFrame) for item in menu: widget.addItem(item) else: #widget = CCP4Widgets.CLineEdit(self.qFrame) widget = QtGui.QLineEdit(self.qFrame) if widget is not None: widget.setObjectName(str(quali)) iRow = iRow + 1 #print 'drawQualifiers',quali,qType,widget,iRow self.qFrame.layout().addWidget(widget,iRow,1) label = QtGui.QLabel(quali,self.qFrame) self.qFrame.layout().addWidget(label,iRow,0) if qType == types.BooleanType: self.connect(widget,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),quali)) else: self.connect(widget,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),quali)) #self.connect(widget,QtCore.SIGNAL('rightMousePress'),functools.partial(self.emit,QtCore.SIGNAL('rightMousePress'),cls.__name__)) except: e.append(self.__class__,102,quali) if len(e)>0: e.warningMessage() def setDefaultQualifiers(self): cls = DATAMANAGER().getClass(self.currentClassName) if cls is None: return obj = cls() if isinstance(cls,CCP4Data.CCollection): self.setQualifiers(obj.subItemObject().qualifiers()) else: self.setQualifiers(obj.qualifiers()) def setQualifiers(self,qualifiers): e = CErrorReport() if self.qFrame is None: return #print 'setObjectQualifiers',qualifiers.items() for key,value in qualifiers.items(): try: widget = self.qFrame.findChild(QtGui.QWidget,key) #print 'setQualifiers',key,type(widget) if widget is not None and value is not NotImplemented: if isinstance(widget,QtGui.QLineEdit): if value is None: widget.setText('') else: widget.setText(str(value)) elif isinstance(widget,QtGui.QCheckBox): if value is None or value is NotImplemented: if widget.isTristate(): widget.setCheckState(QtCore.Qt.PartiallyChecked) else: widget.setChecked(value) else: e.append(self.__class__,103,key) except: e.append(self.__class__,103,key) if len(e)>0: e.warningMessage() def changeDataClass(self,clsName): if clsName is None: return #print 'changeDataClass',self.currentClassName self.classLabel.setText(clsName) cls = DATAMANAGER().getClass(clsName) if cls is None: return self.drawQualifiers(cls) self.currentClassName = clsName self.setDefaultQualifiers() def set(self,dataObj=None): from CCP4XtalData import CProgramColumnGroup if dataObj is None or not isinstance(dataObj,CCP4Data.CData): return self.nameEditor.setText(str(dataObj.objectName())) if isinstance(dataObj,CCP4Data.CCollection): self.collectionFrame.set(dataObj) self.changeDataClass(dataObj.subItemClass().__name__) self.classBrowser.setSelectedClass(dataObj.subItemClassName()) else: self.collectionFrame.set() self.changeDataClass(str(dataObj.__class__.__name__)) self.classBrowser.setSelectedClass(str(dataObj.__class__.__name__)) self.setColumnGroupWidget(dataObj) if isinstance(dataObj,CCP4Data.CCollection): self.setQualifiers(dataObj.subItemQualifiers()) else: self.setQualifiers(dataObj.qualifiers()) def unSet(self): self.currentClassName = None self.nameEditor.setText('') self.nameCombo.clearEditText() self.classLabel.setText('') self.deleteQualiferWidget() def getQualifierValue(self,name=None): widget = self.qFrame.findChild(QtGui.QWidget,name) if widget is None: return None if isinstance(widget,QtGui.QCheckBox): if widget.isTristate() and (widget.checkState() == QtCore.Qt.PartiallyChecked): return None elif widget.isChecked(): return 'True' else: return 'False' else: return str(widget.text()) def setEditMode(self,container=False): # Expect that if current object is container then disallow editing # List and class type if container: idx = 1 else: idx = 0 self.containerFrameStack.setCurrentIndex(idx) self.nameStack.setCurrentIndex(idx) def setColumnGroupWidget(self,currentObject): #print 'deEditor.setColumnGroupWidget',currentObject.objectName(),type(currentObject) from CCP4XtalData import CProgramColumnGroup if currentObject is None or not isinstance(currentObject,CProgramColumnGroup): self.columnGroupWidget.setVisible(False) self.columnGroupWidget.blockSignals(True) else: self.columnGroupWidget.blockSignals(True) self.columnGroupWidget.clear() data = currentObject.getColumnGroupQualifier() #print 'deEditor.setColumnGroupWidget data',data self.columnGroupWidget.set(data) self.columnGroupWidget.setVisible(True) self.columnGroupWidget.blockSignals(False) def setNameComboDefault(self,name): self.nameCombo.removeItem(0) self.nameCombo.insertItem(0,name) self.nameCombo.setCurrentIndex(0) def getName(self): if self.nameStack.currentIndex() == 1: return str(self.nameCombo.currentText()) else: return str(self.nameEditor.text()) class CCollectionFrame(QtGui.QFrame): CONTAINER_CLASS_MENU = ['single object','a list (CList)','a dictionary (CDict)','a table(CTable)'] CONTAINER_CLASS_NAME = [ None,'CList','CDict','CTable'] def __init__(self,parent): QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QHBoxLayout()) self.layout().setSpacing(CDefEd.MARGIN) self.layout().setContentsMargins(CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN,CDefEd.MARGIN) self.layout().addWidget(QtGui.QLabel('Make the object a',self)) self.classCombo =CCP4Widgets.CComboBox(self) self.classCombo.setMaximumWidth(200) for item in CCollectionFrame.CONTAINER_CLASS_MENU: self.classCombo.addItem(item,QtCore.QVariant(item)) self.layout().addWidget(self.classCombo ) self.stack = QtGui.QStackedWidget(self) dummy = QtGui.QLabel('',self) self.stack.addWidget(dummy) self.listFrame = QtGui.QFrame(self) self.listFrame.setLayout(QtGui.QVBoxLayout()) listDefaultLayout = QtGui.QHBoxLayout() listDefaultLayout.addWidget(QtGui.QLabel('List default value',self)) self.listDefault = CCP4Widgets.CLineEdit(self) listDefaultLayout.addWidget(self.listDefault) self.listFrame.layout().addLayout(listDefaultLayout) listQualifiersLayout = QtGui.QHBoxLayout() listQualifiersLayout.addWidget(QtGui.QLabel('List of.. min length:')) self.minLength = CCP4Widgets.CLineEdit(self) validator = QtGui.QIntValidator(self) validator.setBottom(0) self.minLength.setValidator(validator) listQualifiersLayout.addWidget(self.minLength) listQualifiersLayout.addWidget(QtGui.QLabel('max length:',self)) self.maxLength = CCP4Widgets.CLineEdit(self) validator = QtGui.QIntValidator(self) validator.setBottom(0) self.maxLength.setValidator(validator) listQualifiersLayout.addWidget(self.maxLength) self.compare = CCP4Widgets.CComboBox(self) self.compare.setEditable(False) self.compare.addItems(['No comparison','Increasing values','Decreasing values']) listQualifiersLayout.addWidget(self.compare) self.listFrame.layout().addLayout(listQualifiersLayout) self.stack.addWidget(self.listFrame) dictDefaultFrame = QtGui.QFrame(self) dictDefaultFrame.setLayout(QtGui.QHBoxLayout()) dictDefaultFrame.layout().addWidget(QtGui.QLabel('Dictionary default',self)) self.dictDefault = CCP4Widgets.CLineEdit(self) dictDefaultFrame.layout().addWidget(self.dictDefault) self.stack.addWidget(dictDefaultFrame) tableDefaultFrame = QtGui.QFrame(self) tableDefaultFrame.setLayout(QtGui.QHBoxLayout()) tableDefaultFrame.layout().addWidget(QtGui.QLabel('Table default',self)) self.tableDefault = CCP4Widgets.CLineEdit(self) tableDefaultFrame.layout().addWidget(self.tableDefault) self.stack.addWidget(tableDefaultFrame) self.layout().addWidget(self.stack) import functools self.connect(self.classCombo,QtCore.SIGNAL('currentIndexChanged(int)'),self.updateStack) self.connect(self.dictDefault,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),'default')) self.connect(self.listDefault,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),'default')) self.connect(self.minLength,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),'listMinLength')) self.connect(self.maxLength,QtCore.SIGNAL('editingFinished()'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),'listMaxLength')) self.connect(self.compare,QtCore.SIGNAL('currentIndexChanged(int)'),functools.partial(self.emit,QtCore.SIGNAL('qualifierEdited'),'listCompare')) self.connect(self.minLength,QtCore.SIGNAL('rightMousePress'),functools.partial(self.emit,QtCore.SIGNAL('rightMousePress'))) self.connect(self.maxLength,QtCore.SIGNAL('rightMousePress'),functools.partial(self.emit,QtCore.SIGNAL('rightMousePress'))) self.connect(self.compare,QtCore.SIGNAL('rightMousePress'),functools.partial(self.emit,QtCore.SIGNAL('rightMousePress'))) def updateStack(self,indx): #print 'CCollectionFrame.updateStack',indx self.stack.setCurrentIndex(indx) def mousePressEvent(self,event): if event.button() == QtCore.Qt.RightButton: self.emit(QtCore.SIGNAL('rightMousePress'),event) event.accept() else: event.ignore() def set(self,dataObj=None): self.blockSignals(True) self.classCombo.blockSignals(True) if dataObj is not None: if isinstance(dataObj,CCP4Data.CList): self.setList(dataObj) elif isinstance(dataObj,CCP4Data.CDict): self.setDict(dataObj) elif isinstance(dataObj,CCP4Data.CTable): self.setTable(dataObj) else: self.setSingle() else: self.setSingle() self.updateStack(self.classCombo.currentIndex()) self.blockSignals(False) self.classCombo.blockSignals(False) def setSingle(self): self.classCombo.setCurrentIndex(0) self.minLength.setText('') self.maxLength.setText('') self.compare.setCurrentIndex(0) self.listDefault.setText('') self.dictDefault.setText('') def setList(self,dataObj): self.classCombo.setCurrentIndex(1) qualis = dataObj.qualifiers() self.minLength.setValue(qualis.get('listMinLength',None)) self.maxLength.setValue(qualis.get('listMaxLength',None)) compare = qualis.get('listCompare',None) if compare is None: self.compare.setCurrentIndex(0) elif compare == 1: self.compare.setCurrentIndex(1) elif compare == -1: self.compare.setCurrentIndex(2) self.listDefault.setText(str(qualis.get('default'))) self.dictDefault.setText('') def setDict(self,dataObj): self.classCombo.setCurrentIndex(2) qualis = dataObj.qualifiers() self.minLength.setText('') self.maxLength.setText('') self.compare.setCurrentIndex(0) self.listDefault.setText('') self.dictDefault.setText(str(qualis.get('default'))) def setTable(self,dataObj): self.classCombo.setCurrentIndex(3) def getQualifiers(self): qualis = {} currentIndex = self.classCombo.currentIndex() default = None if currentIndex == 1: t = str(self.minLength.text()) if len(t)>0: qualis['listMinLength'] = int(t) else: qualis['listMinLength'] = None t = str(self.maxLength.text()) if len(t)>0: qualis['listMaxLength'] = int(t) else: qualis['listMaxLength'] = None if qualis['listMinLength'] is not None and qualis['listMaxLength'] is not None and qualis['listMinLength'] > qualis['listMaxLength']: t = qualis['listMinLength'] qualis['listMinLength'] = qualis['listMaxLength'] qualis['listMaxLength'] = t print 'swapped listMinLength/listMaxLength',qualis['listMinLength'],qualis['listMaxLength'] ic = self.compare.currentIndex() if ic == 0: qualis['compare'] = None elif ic == 1: qualis['compare'] = 1 elif ic == 2: qualis['compare'] = -1 #print 'CCollectionFrame getQualifiers',qualis default = str(self.listDefault.text()) elif currentIndex == 2: default = str(self.dictDefault.text()) if default is not None: try: exec("qualis['default']="+default) except: qualis['default'] = NotImplemented #print 'CCollectionFrame.getQualifiers',qualis return qualis def getContainerClassName(self): return CCollectionFrame.CONTAINER_CLASS_NAME[self.classCombo.currentIndex()] class CClassInfo(QtGui.QTextDocument): def __init__(self,className=None,parent=None): QtGui.QTextDocument.__init__(self,parent) self._className = None self.setClassName(className) def setClassName(self,clsName): from CCP4DataManager import DATAMANAGER cls = DATAMANAGER().getClass(clsName) if cls is not None: self._className=clsName self.setPlainText(self.getInfo()) self.setHtml(self.getInfo()) def getClass(self): from CCP4DataManager import DATAMANAGER return DATAMANAGER().getClass(self._className) def getClassPathText(self): cls = self.getClass() if cls is None: return '' from CCP4Data import baseClassList baseClassList = baseClassList(cls) text = '' for clsItem in baseClassList: module = str(clsItem.__module__) name = clsItem.__name__ text = text + ''+module+'.'+name+' -> ' return text[0:-4] def getContentsText(self): import CCP4Data cls = self.getClass() if cls is None: return '' text = '' + key + ' | ' + className + ' |
'+quali+' | '+dType+' | '+val+' | '+desc+' |