""" CCP4TaskWidget.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 Jan 2010 - Create CCP4TaskWidget prototype """ ##@package CCP4TaskWidget (QtGui) Widget to view CCP4 tasks import os import re import traceback import functools from PyQt4 import QtGui, QtCore from qtgui import CCP4Widgets from core.CCP4Modules import * from core.CCP4ErrorHandling import * from core.CCP4DataManager import DATAMANAGER from core.CCP4Config import DEVELOPER MARGIN = 1 WIDTH = 600 PADDING_ALLOWANCE = 0 LINEHEIGHT = 20 USER_NOVICE = 0 USER_REGULAR = 1 USER_EXPERT = 2 def whatNext(self, jobId=None, childJobs=[], childTaskName=None): return [] class CFolderAttributes: def __init__(self): self.atts = {'all' : {'editable' : True}} #print 'CFolderAttributes.__init__' def setAttribute(self, attribute='editable', folderFunction='all', value=None): if not attribute in ['editable'] or folderFunction is None or value is None: print 'Invalid input to CFolderAttributes.setAttribute', attribute, folderFunction, value return if not self.atts.has_key(folderFunction): self.atts[folderFunction] = {} self.atts[folderFunction][attribute] = value #print 'CFolderAttributes.setAttribute',self.atts def attribute(self, attribute='editable', folderFunction='all'): if not self.atts.has_key(folderFunction): folderFunction = 'all' if self.atts[folderFunction].has_key(attribute): #print 'CFolderAttributes.attribute',attribute,folderFunction,self.atts[folderFunction][attribute] return self.atts[folderFunction][attribute] else: return NotImplemented def allAttributes(self,folderFunction='all'): if not self.atts.has_key(folderFunction): return self.atts['all'] else: atts = {} atts.update(self.atts['all']) atts.update(self.atts[folderFunction]) return atts class CTaskWidgetDrawMethods: ERROR_CODES = {100 : {'description' : 'No definition found for data name'}, 101 : {'description' : 'Parameter name not recognised in setMenuText'}, 102 : {'description' : 'Wrong number of menu text items in setMenuText'}, 103 : {'description' : 'Reference to unknown enumerator value(s) in setMenuText'}, 104 : {'description' : 'Parameter name not recognised in setTooltip'}, 105 : {'description' : 'Parameter name not recognised in setLabel'}} STACK_LAYOUT = True def __init__(self): self.toggleList = [] self.subFrame = None self.stack = None self.tabWidget = None self.currentFolder = None self.currentFolderLayout = None self.jobTitleWidget = None self.ignoreInput = False def openTabFrame(self, title=None, toolTip=None): if self.tabWidget is None: self.tabWidget = QtGui.QTabWidget(self) self.currentFolderLayout.addWidget( self.tabWidget) else: self.tabWidget.widget(self.tabWidget.count()-1).layout().addStretch(2) frame = QtGui.QFrame() frame.setLayout(QtGui.QVBoxLayout()) frame.layout().setSpacing(MARGIN) frame.layout().setContentsMargins(MARGIN, MARGIN, MARGIN, MARGIN) self.tabWidget.addTab(frame, title) if toolTip is not None: self.tabWidget.setTabToolTip(self.tabWidget.count() - 1, toolTip) else: #Worth setting title astool tip so it is readable if title display is trucated self.tabWidget.setTabToolTip(self.tabWidget.count() - 1, title) def closeTabFrame(self): self.tabWidget.widget(self.tabWidget.count() - 1).layout().addStretch(2) self.tabWidget = None def openSubFrame(self, toggle=[], toggleFunction=[], frame=False, title=None): if self.ignoreInput: return self.closeSubFrame() if self.currentFolder is None: print 'ERROR in openSubFrame: there is not current open folder' return [1, 'ERROR in openSubFrame: there is not current open folder'] self.subFrame = CSubFrame(self) self.subFrame.setObjectName('tasksubframe') self.currentFolderLayout.addWidget(self.subFrame) layout = QtGui.QVBoxLayout() layout.setSpacing(MARGIN) layout.setContentsMargins(MARGIN, MARGIN, MARGIN, MARGIN) self.subFrame.setLayout(layout) self.noChildFrame = False #print 'openSubFrame args',self.subFrame,arg if len(toggle) == 2: self.setToggle(target=self.subFrame, parameter=toggle[0], state=toggle[1]) #print 'setToggle',target elif len(toggle) == 3: self.setToggle(target=self.subFrame, parameter=toggle[0], state=toggle[1], values=toggle[2]) elif len(toggleFunction) == 2: self.setToggleFunction(target=self.subFrame, toggleFunction=toggleFunction[0], parameterList=toggleFunction[1]) if frame: self.subFrame.setFrameShape(QtGui.QFrame.StyledPanel) self.noChildFrame = True if title is not None: self.createLine(['advice', title]) return self.subFrame def closeSubFrame(self): if self.ignoreInput: return if self.subFrame is not None and self.noChildFrame: children = self.subFrame.findChildren(CCP4Widgets.CComplexLineWidget) for child in children: child.setFrameShape(QtGui.QFrame.NoFrame) self.subFrame.layout().update() self.subFrame = None def openStack(self, controlVar=None): if self.ignoreInput: return if self.stack is not None: self.closeStack() if self.STACK_LAYOUT: self.stack = CStackedLayout(self, controlVar) else: self.stack = CStackedWidget(self, controlVar) return self.stack def closeStack(self): if self.ignoreInput: return if self.stack is not None: #print 'closeStack',self.stack,self.currentFolderLayout if self.STACK_LAYOUT: if self.subFrame is not None: self.subFrame.layout().addLayout(self.stack) else: self.currentFolderLayout.addLayout(self.stack) else: if self.subFrame is not None: self.subFrame.layout().addWidget(self.stack) else: self.currentFolderLayout.addWidget(self.stack) #self.stack.update() self.stack = None def createTitleLine(self, name='TITLE'): if self.ignoreInput: return self.createLine(['label', 'Comment', 'widget', name]) def createLine(self, definition=[], appendLine=None, toggle=[], toggleFunction=[]): if self.ignoreInput: return container = self.parentTaskWidget()._container line = CTaskLine(self) if len(definition) > 0: #if self.parentTaskWidget().layoutMode == 'TAB': # rv = line.draw(definition=definition,dataContainer=container,setupFolder=self.currentFolder.setupUpdateFolder,attributes=self.currentFolder._attributes) #else: rv = line.draw(definition=definition, dataContainer=container, attributes=self.currentFolder._attributes) self.myException.extend(rv, stack=False) else: rv = [] if len(rv) > 0: line.deleteLater() else: if appendLine is not None: appendLine.addWidget(line) elif self.stack is not None: self.stack.addWidget(line) elif self.subFrame is not None: self.subFrame.layout().addWidget(line) elif self.tabWidget is not None: self.tabWidget.widget(self.tabWidget.count() - 1).layout().addWidget(line) else: self.currentFolderLayout.addWidget(line) #print 'CTaskWidgetDrawMethods.createLine toggle',self.subFrame, definition if len(toggle) > 0: if len(toggle) == 2: self.setToggle(line, toggle[0], toggle[1], [True]) else: self.setToggle(line, toggle[0], toggle[1], toggle[2]) if len(toggleFunction) > 0: self.setToggleFunction(line, toggleFunction[0], toggleFunction[1]) return line def applyToggles(self): if self.ignoreInput: return for toggle in self.toggleList: if not toggle.connected: toggle.handleSignal() toggle.makeConnection() childStacks = self.findChildren(CStackedLayout) for stack in childStacks: stack.update() def setToggle(self, target=None, parameter=None, state='open', values =[]): if self.ignoreInput: return #print 'setToggle',target if target is None or parameter is None or len(values) == 0: return self.toggleList.append(CToggle(self, target, parameter, state, values)) def setToggleFunction(self, target=None, toggleFunction=None, parameterList=[]): if self.ignoreInput: return if target is None or toggleFunction is None or len(parameterList) == 0: return self.toggleList.append(CToggle(self, target, parameterList=parameterList, toggleFunction=toggleFunction)) def createRadioGroup(self, label=None, itemList=[], objectName=None): if self.ignoreInput: return if objectName is not None: varObj = self.parentTaskWidget()._container.getObject(objectName) itemList = varObj.qualifiers('enumerators') else: varObj = None line = QtGui.QFrame(self) line.setLayout(QtGui.QHBoxLayout()) if label is not None: line.layout().addWidget(QtGui.QLabel(label, self)) group = QtGui.QButtonGroup(self) idx = -1 for item in itemList: idx = idx + 1 button = QtGui.QRadioButton(item, self) group.addButton(button, idx) line.layout().addWidget(button) self.currentFolderLayout.addWidget(line) return group def createJobTitle(self, followFrom=True): from core import CCP4TaskManager self.jobHeaderFrame = self.parentTaskWidget().openSubFrame(frame=[False]) self.jobHeaderFrame.setObjectName('jobHeaderFrame') # so that it can be styled if self.ignoreInput: return #print 'createJobTitle editable',self.parentTaskWidget().folderAttributes.attribute('editable'),self.parentTaskWidget().jobId() line = self.parentTaskWidget().createLine(['label', 'Job title', 'widget', 'jobTitle']) try: self.jobTitleWidget = line.layout().itemAt(1).widget().widget except: return t = self.getWidget('jobTitle').model if t is None: return #print self.getWidget('jobTitle'), t if not self.parentTaskWidget().folderAttributes.attribute('editable'): #Its an old job and job title may have been changed in database since saving params.xml title = PROJECTSMANAGER().db().getJobInfo(jobId=self.parentTaskWidget().jobId(), mode='jobtitle') #print 'createJobTitle title from db',title if title is not None: t.set(title) if not t.isSet() or len(t) == 0: t.set(CCP4TaskManager.TASKMANAGER().getShortTitle(self.parentTaskWidget().taskName())) self.connect(self.jobTitleWidget, QtCore.SIGNAL('editingFinished()'), self.saveTitle) self.jobTitleWidget.setToolTip('Short description of job to appear in Job list') if followFrom and self.parentTaskWidget().folderAttributes.attribute('editable'): self.parentTaskWidget().createLine(['widget','followFrom']) self.parentTaskWidget().closeSubFrame() def createPatchFrame(self): if self.ignoreInput: return taskWidget = self.parentTaskWidget() # Force 'fix' of the patchSelection object before drawing try: patchSelectionObj = taskWidget._container.guiAdmin.patchSelection except: return patchSelectionObj.set(patchSelectionObj.fix({'taskName' : taskWidget.taskName(), 'patch' : patchSelectionObj.get('patch') })) if len(patchSelectionObj.getPatchList()) > 0: taskWidget.createLine(['widget', 'patchSelection']) self.connect(patchSelectionObj, QtCore.SIGNAL('dataChanged'), self.handlePatchChange) self.handlePatchChange() def handlePatchChange(self): taskWidget = self.parentTaskWidget() #print 'CTaskWidget.handlePatchChange patch',taskWidget._container.guiAdmin.patchSelection.patch try: if not taskWidget._container.guiAdmin.patchSelection.patch.isSet(): return except: return controlParameters = COMFILEPATCHMANAGER().getComFileControlParameters(name=taskWidget._container.guiAdmin.patchSelection.patch.__str__()) #print 'CTaskWidget.handlePatchChange',controlParameters taskWidget._container.controlParameters.update(controlParameters) #print 'CTaskWidget after update',taskWidget._container.controlParameters for key in controlParameters.dataOrder(): widget = self.findChild(CCP4Widgets.CViewWidget,key) if widget is not None: widget.updateViewFromModel() def saveTitle(self): from core import CCP4Modules jobId = self.parentTaskWidget().jobId() if jobId is None: return text = str(self.jobTitleWidget.text()) print 'CTaskWidgetDrawMethods.saveTitle', jobId,text try: CCP4Modules.PROJECTSMANAGER().db().updateJob(jobId=jobId, key='jobTitle', value=text) except: pass class CTaskWidget(QtGui.QFrame): EDITOR = False AUTOPOPULATEINPUT = True USEPLUGIN = None ERROR_CODES = {101 : {'description' : 'Error updating view from model'}, 102 : {'description' : 'Error updating model from view'}, 103 : {'description' : 'Error in cootFix'}} def __init__(self, parent=None, title=None, projectId=None, jobId=None, layoutMode=None): QtGui.QFrame.__init__(self, parent) from core import CCP4Container from core import CCP4Modules self.setTitle(title) self.helpFile = '' self.programHelpFile = '' # Dict of sub-job widgets (eg workflows) self.subJobTaskWidgets = {} self.paramsFileName = None # For special cases (eg workflows) do not draw input data frame self.excludeInputData = False self.paramsList = [] self.widgetLookup = {} self.layoutMode = layoutMode if layoutMode is not None and layoutMode in ['TAB', 'FOLDER']: self.layoutMode = layoutMode else: self.layoutMode = str(CCP4Modules.PREFERENCES().TASK_WINDOW_LAYOUT) # Project is projectName as this is used by the CDataFileView most frequently self._projectId = projectId self._jobId = jobId self._jobNumber = None self._container = CCP4Container.CContainer() self.folderAttributes = CFolderAttributes() self.contextMenuWidget = QtGui.QMenu(self) self.widgetWithContextMenu = [] self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) if self.layoutMode == 'TAB': self.widget = CTabTaskWidget(parent=self) self.layout().addWidget(self.widget) else: self.scrollArea= QtGui.QScrollArea(self) self.layout().addWidget(self.scrollArea) self.widget = CFolderTaskWidget(parent=self) self.scrollArea.setWidget(self.widget) self.scrollArea.setWidgetResizable(1) self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) #print 'CTaskWidget self',self,'scrollArea',self.scrollArea,'widget',self.widget,'widget.parent',self.widget.parent(),self.widget.parent().parent() self.messageScrollArea= QtGui.QScrollArea(self) self.messageScrollArea.setMinimumHeight(40) self.messageScrollArea.setMaximumHeight(40) #self.messageScrollArea.setMinimumWidth(WIDTH) #self.messageScrollArea.setMaximumWidth(WIDTH) self.messageScrollArea.setObjectName('messageScrollArea') self.message = QtGui.QLabel(self) self.message.setObjectName('errorMessage') self.message.setWordWrap(True) self.messageScrollArea.setWidget(self.message) self.messageScrollArea.setWidgetResizable(1) self.layout().addWidget(self.messageScrollArea) self.messageStack = [] def setMessage(self, text='', parameter=None): if text is None: text = '' self.message.setText(text) self.messageStack.append([parameter,text]) #print 'setMessage',text,parameter,self.messageStack def unsetMessage(self, parameter=None): if len(self.messageStack) == 0: return if self.messageStack[-1][0] == parameter: del self.messageStack[-1] else: delFrom = None for ii in range(len(self.messageStack)-2,-1,-1): if self.messageStack[ii][0] == parameter: delFrom = ii if delFrom is not None: #print 'unsetMessage deleting multi levels of stack',self.messageStack,delFrom del self.messageStack[delFrom:-1] #print 'unsetMessage',parameter,self.messageStack if len(self.messageStack) > 0: self.message.setText(self.messageStack[-1][1]) def updateMessage(self, text='', parameter=None): for ii in range(len(self.messageStack)): if self.messageStack[ii][0] == parameter: if text is None: text = '' self.messageStack[ii][1] = text if len(self.messageStack) > 0: self.message.setText(self.messageStack[-1][1]) #print 'updateMessage',text,parameter,self.messageStack def setDefaultParameters(self): # Reimplement in tasks to set initial parameters pass def setDefaultFiles(self): # Reimplement in tasks to set initial files # This is called after setFollowJobId() so can override # and files set by the system pass def setParamsFileName(self, fileName): self.paramsFileName = fileName def setSelection(self, selection): self.selection = selection def setFollowJobId(self, jobId, force=True): self._container.guiAdmin.followFrom = jobId widget = self.getWidget('followFrom') if widget is not None: widget.updateInputFiles(jobId, self._projectId, force) widget.updateViewFromModel() def loadControlParameters(self,jobId): print 'CTaskWidget.loadControlParameters', jobId from core import CCP4Container defFile = PROJECTSMANAGER().makeFileName(jobId, 'PARAMS') if not os.path.exists(defFile): defFile1 = PROJECTSMANAGER().makeFileName(jobId, 'JOB_INPUT') if not os.path.exists(defFile1): print 'Can not copy parameters from other job - parameters file ', defFile, 'does not exist' return else: defFile = defFile1 tmpContainer = CCP4Container.CContainer() tmpContainer.loadDataFromXml(defFile, check=False, loadHeader=False) self._container.controlParameters.copyData(tmpContainer.controlParameters) for key in self._container.controlParameters.dataOrder(): widget = self.getWidget(key) if widget is None: print 'loadControlParameters no widget for', key else: widget.updateViewFromModel() print 'loadControlParameters updated', key def isEditable(self): return self.folderAttributes.attribute('editable') def draw(self): rv = self.widget.draw() self.widget.show() return rv def visibleFolder(self): if self.layoutMode == 'TAB': return str(self.widget.tabText(self.widget.currentIndex())) else: return None def setVisibleFolder(self,title=None,index=None): if self.layoutMode != 'TAB': return if index is None: for ii in range(self.widget.count()): if title == str(self.widget.tabText(ii)): index = ii break if index is not None: self.widget.setCurrentIndex(index) def openFolder(self,folderFunction='general', title=None, toggle=[], toggleFunction=[], level=USER_NOVICE, autoSelection={}, drawFolder=None, **kw): #print 'CtaskWidget.openFolder',folderFunction,self.excludeInputData if self.excludeInputData and folderFunction == 'inputData': self.widget.ignoreInput = True return else: self.widget.ignoreInput = False if title == None: if folderFunction == 'protocol': title = self.title() elif folderFunction == 'inputData': title = 'Input data' elif folderFunction == 'outputData': title = 'Output data' attribs = self.folderAttributes.allAttributes(folderFunction) attribs['level'] = level rv = self.widget.openFolder(folderFunction=folderFunction, title=title, toggle=toggle, toggleFunction=toggleFunction, attributes=attribs, drawFolder=drawFolder, keywords=kw) if len(autoSelection) > 0: self.autoGenerate(container=self.container.controlParameters, selection=autoSelection) return rv def closeFolder(self): self.widget.closeFolder() def createLine(self, definition=[], appendLine=None, toggle=[], toggleFunction=[]): return self.widget.createLine(definition=definition, appendLine=appendLine, toggle=toggle, toggleFunction=toggleFunction) def openStack(self, controlVar=None): return self.widget.openStack(controlVar=controlVar) def closeStack(self): self.widget.closeStack() def openTabFrame(self, title, toolTip=None): self.widget.openTabFrame(title=title, toolTip=toolTip) def closeTabFrame(self): self.widget.closeTabFrame() def createRadioGroup(self, label=None, itemList=[]): return self.widget.createRadioGroup(label=label, itemList=itemList) def createTitleLine(self, name='TITLE'): self.widget.createTitleLine(name=name) def openSubFrame(self, toggle=[], toggleFunction=[], frame=False, title=None): #print 'CTaskWidget.openSubFrame',args return self.widget.openSubFrame(toggle=toggle, toggleFunction=toggleFunction, frame=frame, title=title) def closeSubFrame(self): self.widget.closeSubFrame() def setContainer(self, container=None): self._container = container def getContainer(self): return self._container container = property(getContainer, setContainer) def setProjectId(self, projectId=None): self._projectId = projectId def projectId(self): return self._projectId def setJobId(self, jobId): self._jobId = jobId self._jobNumber= PROJECTSMANAGER().db().getJobInfo(jobId = jobId, mode='jobnumber') def jobId(self): return self._jobId def jobNumber(self): return self._jobNumber def taskName(self): return self.TASKNAME def setTitle(self, title): self._title = title def title(self): if self._title is not None: return self._title elif hasattr(self, 'TASKTITLE'): return self.TASKTITLE else: return self.__class__.__name__ def isEditor(self): return self.EDITOR def autoPopulateInput(self): return self.AUTOPOPULATEINPUT def getParams(self, paramValues={}): from core import CCP4Data for key, value in paramValues.items(): widget = self.getWidget(key) if widget is not None: if isinstance(value, CCP4Data.CData): ret_value = widget.model.getDataObjects() else: ret_value = widget.getValue() paramValues[key] = ret_value def setDefaultParams(self): if self._container is not None: return 0 self.setParams(self._container.getDataObjects()) def setParams(self,paramValues={}): for key,value in paramValues.items(): widget = self.getWidget(key) if widget is not None: if isinstance(widget, CCP4Widgets.CViewWidget): widget.setModel(value) else: widget.setValue(value) def setProgramHelpFile(self, helpFile): self.programHelpFile = helpFile def setHelpFile(self, helpFile): self.helpFile = helpFile def populateContextMenu(self, name, helpFile, helpTarget, globalX, globalY): #print 'CTaskWidget.populateContextMenu',name,helpTarget,globalX,globalY from qtgui import CCP4GuiUtils self.widgetWithContextMenu = [name, helpFile, helpTarget] self.contextMenuWidget.clear() CCP4GuiUtils.populateMenu(self, self.contextMenuWidget, ['help'], default_icon='') self.contextMenuWidget.popup(QtCore.QPoint(globalX, globalY)) def getActionDef(self,name=''): if name == 'help': return dict(text = 'Help', tip = 'Help', slot = self.help) def help(self): #print 'CTaskWidget.help',self.widgetWithContextMenu try: helpPath = os.path.join(os.environ['CCP4'], 'docs') except: QtGui.QMessageBox.warning(None, self.windowTitle(), 'Can not access help files - CCP4 not setup') helpPath = os.path.join(helpPath, self.widgetWithContextMenu[1] + '.html') if not os.path.exists(helpPath): QtGui.QMessageBox.warning(None, self.windowTitle(), 'Help file not found: ' + helpPath) return WEBBROWSER().loadWebPage(helpPath) ''' def updateModelFromView(self,textOnly=False): # Not convinced this is needed and sledge-hammer updateModelFromView() of some # widgets (eg CImportUnmerged) very bad as trashes gui-generalted data (such as cell params) return def updateModelFromView(self,textOnly=False): rv = CErrorReport() from qtgui.CCP4Widgets import CViewWidget from qtgui.CCP4ContainerView import CContainerView toggledFrame = {} for t in self.widget.toggleList: toggledFrame[t.target] = t.getTargetVisibility() widgetList = self.findChildren(CViewWidget) #print 'CTaskWidget.updateModelFromView',textOnly,widgetList for widget in widgetList: #print widget.model.objectName(), w = widget.parent() isVis = True while isVis and not isinstance(w,(CTaskWidget,CContainerView,QtGui.QDialog,QtGui.QMainWindow)): #print w,toggledFrame.get(w,True) ,'*', if not toggledFrame.get(w,True): isVis = False w = w.parent() #print 'isVis',isVis,widget.getValue() if isVis: try: if textOnly: widget.updateModelFromText() else: widget.updateModelFromView() except: rv.append(self.__class__,102,str(widget.objectName()),stack=False) return rv ''' def updateModelFromView(self, textOnly=False): # Called when user clicks run - try to fix any text widget with focus that has not updated # the model since has not had the 'finished' signal from user hitting return or moving focus rv = CErrorReport() fW = QTAPPLICATION().focusWidget() if fW is not None: if isinstance(fW,CCP4Widgets.CLineEdit): fW.emit(QtCore.SIGNAL('editingFinished()')) elif isinstance(fW,CCP4Widgets.CTextEdit): fW.emit(QtCore.SIGNAL('textChanged()')) return rv ''' w = fW.parent() while w is not None: if isinstance(w,(CCP4Widgets.CStringView,CCP4Widgets.CFloatView,CCP4Widgets.CIntView)): try: print 'CTaskWidget.updateModelFromView updating model from view for',w.model.objectName(),'widget value:',w.getValue(),'old model value:',w.model w.updateModelFromView() except: pass return rv else: w = w.parent() ''' def updateViewFromModel(self): rv = CErrorReport() for param in self.paramsList: try: widgetList = self.findWidget(param) #print 'CTaskWidget.updateViewFromModel',widget,widget.model.get0() for widget in widgetList: widget.blockSignals(True) widget.updateViewFromModel() widget.blockSignals(False) except: rv.append(self.__class__, 101, str(widget.objectName())) #print 'CTaskWidget.updateViewFromModel errors:',rv.report() return rv def setup(self): return 0 def createWidget(self, name=None, widgetQualifiers={}, helpTarget=None): model = self._container.find(name) if model is None: raise CException(self.__class__, 100, name) wQualifiers = {} wQualifiers.update(widgetQualifiers) wQualifiers.update(model.qualifiers()) #print 'widgetQualifiers',name,wQualifiers widget = DATAMANAGER().widget(model=model, parentWidget=self, qualifiers=wQualifiers, name=name) #print 'CTaskLine widget',name,widget if widget is not None: widget.setObjectName(name) #modelTip = model.qualifiers('toolTip') #if modelTip is not NotImplemented: # widget.setToolTip(modelTip) if model.qualifiers('toolTip') is not NotImplemented: widget.setToolTip(model.qualifiers('toolTip')) ''' if widget.STRETCH > 0: self.layout().setStretch(self.layout().count(),widget.stretchFactor()) doneStretch = True elif widgetQualifiers.get('charWidth',1) < 0: doneStretch = True ''' if helpTarget is not None: # KJS: Change to functools version self.connect(widget, QtCore.SIGNAL('contextMenuRequest'), functools.partial(self.populateContextMenu, name, self.programHelpFile, helpTarget)) return widget def getWidget(self, name=None): return self.widgetLookup.get(name, [None])[0] def setMenuText(self, parameter=None, menuText=None): ''' Set data object qualifier menuText from Python parameter is the name of the data object menuText can be a list which should be same length as the enumerators qualifier or it can be a dict with the enumerator values as keys ''' dataObj = self._container.find(parameter) if dataObj is None: raise CException(self.__class__, 101, str(parameter)) enumerators = dataObj.qualifiers('enumerators') #print 'setMenuText enumerators',parameter,dataObj,enumerators,len(enumerators),len(menuText) if isinstance(menuText,list): if len(menuText) != len(enumerators): raise CException(self.__class__, 102, str(parameter), stack=False) dataObj.setQualifier('menuText', menuText) elif isinstance(menuText, dict): enums = [] menu = [] unrecog = [] for item in enumerators: if menuText.has_key(item): enums.append(item) menu.append(menuText[item]) else: unrecog.append(item) #print 'setMenuText new menu',enums,menu dataObj.setQualifiers({'enumerators' : enums, 'menuText': menu}) if len(unrecog) > 0: return CException(self.__class__, 103, str(parameter)+' ' + str(unrecog)) def setToolTip(self, parameter=None, toolTip=None): dataObj = self._container.find(parameter) if dataObj is None: raise CException(self.__class__, 104, str(parameter)) dataObj.setQualifier('toolTip', toolTip) def setLabel(self, parameter=None, label=None): dataObj = self._container.find(parameter) if dataObj is None: raise CException(self.__class__, 105, str(parameter)) dataObj.setQualifier('label', label) def validate(self): if self.isEditable(): return self.widget.validate() else: return 0 def resetJobCombos(self): return self.widget.resetJobCombos() def fix(self): # Dummy method to be reimplemented in sub-class # Called after user clicks run button and before validate() # Probably should be used mostly to ensure that if there are # possible alternate input files then only one has a set value # (this is to prevent unused files being recorded in database) self.emit(QtCore.SIGNAL('doFix')) rv = CErrorReport() for key, win in self.subJobTaskWidgets.items(): rv.extend(win.fix()) return rv def cootFix(self): from core import CCP4Modules from qtgui import CCP4FileBrowser path = str(CCP4Modules.PREFERENCES().COOT_EXECUTABLE) #print 'CTaskWidget.cootFix',path try: if path is not None and os.path.exists(path): return CErrorReport() except: pass ''' path = CCP4Utils.findCootPath() if path is not None: CCP4Modules.PREFERENCES().COOT_EXECUTABLE.set(fullPath=path) return CErrorReport() ''' self.cootFixDialog = CCP4FileBrowser.CFileDialog(self, 'Find Coot Path', filters=[' (*)'], projectCombo=False) label = QtGui.QLabel("""Sorry - failed to find Coot. Please enter the Coot executable and then 'Run' again.\nBeware you are probably using a CCP4 nightly build that does not include Coot.\nThe Coot executable can also be set in Preferences.""",self) label.setStyleSheet("QLabel { font-weight: bold; border: 2px solid} ") self.cootFixDialog.addWidget(label) self.connect(self.cootFixDialog, QtCore.SIGNAL('selectFile'), self.handleCootFix) self.cootFixDialog.show() self.cootFixDialog.raise_() return CErrorReport(self.__class__, 103) def handleCootFix(self, filePath): #self.cootFixWidget.updateModelFromView() self.cootFixDialog.hide() self.cootFixDialog.deleteLater() if filePath is not None and os.path.exists(filePath): from core import CCP4Modules CCP4Modules.PREFERENCES().COOT_EXECUTABLE.set(filePath) CCP4Modules.PREFERENCES().save() else: pass def isValid(self): invalidList = [] for key, widgetList in self.widgetLookup.items(): for widget in widgetList: if widget.isValid is None: widget.validate() if widget.isValid is not None and not widget.isValid: if getattr(widget, 'model', None) is not None: if widget.model.objectName() != 'fileContent': #print 'CTaskWidget.isValid',widget.model.objectName(),widget.isValid invalidList.append(widget.model) else: invalidList.append(str(widget)) # Apply to any sub-job widgets for key, win in self.subJobTaskWidgets.items(): invalidList.extend(win.isValid()) #print 'CTaskWidget.isValid',invalidList return invalidList def taskValidity(self): return CErrorReport() def getScrollDisplacement(self): scrollArea = getattr(self, 'scrollArea', None) if scrollArea is None: return 0 else: return self.scrollArea.verticalScrollBar().value() def setScrollDisplacement(self, value): scrollArea = getattr(self, 'scrollArea', None) if scrollArea is not None: self.scrollArea.verticalScrollBar().setValue(value) def autoGenerate(self, container=None, selection={}, subFrame=False): from core import CCP4File from core import CCP4Data from core import CCP4Container label = container.qualifiers('guiLabel') defn = container.qualifiers('guiDefinition') if defn is None or defn is NotImplemented: defn = {} if subFrame: frame = self.widget.openSubFrame(frame=True) #print 'subFrame',frame,container.__dict__['_qualifiers'],label,defn, defn.get('toggleParameter',None) if defn.get('toggleParameter', None) is not None: self.widget.setToggle(target=frame, parameter=defn['toggleParameter'], state=defn.get('toggleState', 'open'), values=defn['toggleValues']) if label is not NotImplemented: self.widget.createLine(definition=['advice', label]) #print 'autoGenerate container',repr(container),defn includeParameters = selection.get('includeParameters', []) #print 'CTaskWidget.autoGenerate includeParameters',includeParameters includeWildcards = [] for item in includeParameters: if item.count('*'): includeWildcards.append(item) excludeParameters = selection.get('excludeParameters', []) keyValues = selection.get('keyValues', {}) #-------------------------------------------------------------------- def matchesKeyValues(definition): if len(keyValues) == 0: return True for key,value in keyValues.items(): if not definition.has_key(key) or definition[key] != value: return False return True def matchesWildcard(name): for wildcard in includeWildcards: if re.match(wildcard, name): return True return False #-------------------------------------------------------------------- #print 'CTaskWidget.autoGenerate includeWildcards',includeWildcards for name in container.dataOrder(): # If includeParameters is defined then name should be in that list or match a wildcard in that list # if includeParameters is not defined then name just needs to not be in excludeParameters list #print 'CTaskWidget.autoGenerate',name,len(includeParameters),name in includeParameters,matchesWildcard(name) if (len(includeParameters) > 0 and (name in includeParameters or matchesWildcard(name))) or \ (len(includeParameters) == 0 and name not in selection.get('excludeParameters', [])): model=getattr(container,name) if isinstance(model, CCP4Container.CContainer): self.autoGenerate(model, selection=selection, subFrame=True) else: label = model.qualifiers('guiLabel') defn = model.qualifiers('guiDefinition') if defn is None or defn is NotImplemented: defn = {} if label is None or label is NotImplemented: label = name #print 'autoGenerate',name,repr(model),label,defn if matchesKeyValues(defn): if isinstance(model, CCP4File.CDataFile): line = self.widget.createLine(definition=['widget', name ]) widget = line.findChildren(QtGui.QWidget)[0] elif isinstance(model, CCP4Data.CBoolean): line = self.widget.createLine(definition=['widget', name, 'label' , label]) widget = line.findChildren(QtGui.QWidget)[0] else: line = self.widget.createLine(definition=['label', label, 'widget', name]) widget = line.findChildren(QtGui.QWidget)[1] #widget.setMaximumWidth(WIDTH-116) if defn.get('toggleParameter',None) is not None: self.widget.setToggle(target=line, parameter=defn['toggleParameter'], state=defn.get('toggleState', 'open'), values=defn['toggleValues']) toolTip = model.qualifiers('toolTip') if toolTip is not None and toolTip is not NotImplemented: #print 'toolTip',widgetIndex,line.findChildren(QtGui.QWidget) widget.setToolTip(toolTip) if subFrame: self.closeSubFrame() def handleClosingSubTaskWindow(self, jobName): if self.subJobTaskWidgets.has_key(jobName): fileNameSplit = os.path.split(PROJECTSMANAGER().makeFileName(jobId=self._jobId, mode='JOB_INPUT')) self.subJobTaskWidgets[jobName].saveToXml(fileName=os.path.join(fileNameSplit[0], jobName + '_' + fileNameSplit[1])) del self.subJobTaskWidgets[jobName] def slugify(self, value): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. """ import unicodedata value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) value = unicode(re.sub('[-\s]+', '-', value)) return value def patchOutputFilePaths(self, jobInfo, fileName): from core import CCP4File from core import CCP4Data jobDirectory = os.path.split(os.path.normpath(fileName))[0] dataList = self.container.outputData.dataOrder() for objectName in dataList: dobj = self.container.outputData.find(objectName) partPath = os.path.join(jobInfo['jobnumber']+"_"+jobInfo['projectname']+"_"+objectName+"_"+jobInfo['taskname']) partPath = str(self.slugify(unicode(partPath))) if isinstance(dobj,CCP4File.CDataFile): fullPath = os.path.join(jobDirectory, partPath+"." + dobj.fileExtensions()[0]) dobj.setFullPath(fullPath) elif isinstance(dobj,CCP4Data.COutputFileList): #Empty the current output file list array while len(dobj) > 0: dobj.remove(dobj[-1]) for iItem in range(dobj.qualifiers()['listMaxLength']): dobj.append(dobj.makeItem()) fullPath = os.path.join(jobDirectory, "{}_{}.{}".format(partPath, str(iItem),dobj[-1].fileExtensions()[0])) dobj[-1].setFullPath(fullPath) def saveToXml(self, fileName=None, jobInfo={}): # Assume the updateModelFromView() has been called earlier by project viewer before the # validation of input #self.updateModelFromView() from core import CCP4File from core import CCP4Modules from core import CCP4TaskManager if fileName is None: if self.paramsFileName is not None: fileName= self.paramsFileName else: fileName = CCP4Modules.PROJECTSMANAGER().makeFileName(jobId=self._jobId, mode='JOB_INPUT') jobInfo = {} if self._jobId is not None: try: jobInfo = CCP4Modules.PROJECTSMANAGER().db().getJobInfo(jobId=self._jobId, mode=['taskname', 'jobnumber', 'projectname', 'status', 'projectid']) except: pass f = CCP4File.CI2XmlDataFile(fullPath=fileName) cHeader = self.container.getHeader() if cHeader is not None: f.header.set(cHeader) f.header.setCurrent() f.header.function = 'PARAMS' f.header.jobId.set(self._jobId) f.header.projectId.set(self._projectId) try: f.header.pluginName = CCP4TaskManager.TASKMANAGER().getTaskData(self.taskName())['taskName'] except: print 'Error getting plugin name for taskName',self.taskName() if jobInfo.get('jobnumber', None) is not None: f.header.jobNumber.set(jobInfo['jobnumber']) if jobInfo.get('projectname', None) is not None: f.header.projectName.set(jobInfo['projectname']) #MN set output file paths here, where project, and jobNumber are all known self.patchOutputFilePaths(jobInfo, fileName) bodyEtree = self.container.getEtree() f.saveFile(bodyEtree=bodyEtree) #print 'CTaskWidget.saveTofile DONE',fileName,jobInfo def handleLaunchedJob(self, jobId=None, status=None, taskWidget=None): print 'Dummy method for reimplementation in task that has launched a popout task', jobId, status, taskWidget pass def connectDataChanged(self, name, handle): obj = self.container.find(name) if obj is None: return self.connect(obj,QtCore.SIGNAL('dataChanged'), handle) def findWidget(self, name): return self.widgetLookup.get(name, None) #--------------------------------------------------------------------- class CFolderTaskWidget(QtGui.QFrame, CTaskWidgetDrawMethods): #--------------------------------------------------------------------- MARGIN = 4 ERROR_CODES = {101 : {'description' : 'Error updating GUI widget with model value'}, 105 : {'description' : 'Internal error handing file - no task container'}} def __init__(self, parent=None): QtGui.QFrame.__init__(self,parent) CTaskWidgetDrawMethods.__init__(self) layout = QtGui.QVBoxLayout() #layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize) layout.setContentsMargins(CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN) layout.setSpacing(CFolderTaskWidget.MARGIN) self.setLayout(layout) def parentTaskWidget(self): return self.parent().parent().parent() def draw(self): self.myException = CErrorReport() self.parentTaskWidget().drawContents() self.parentTaskWidget().updateViewFromModel() self.closeFolder() self.layout().addStretch(5) ''' for ii in range(0,self.layout().count()): w = self.layout().itemAt(ii).widget() print 'CTaskWidget.draw',ii,w if w is not None: w.contents.show() w.show() ''' #print 'CTaskWidget.finishDraw',self.isVisible() self.parentTaskWidget().setDefaultParams() self.applyToggles() e = CErrorReport() #print 'CTaskWidget.draw', len(e) e.extend(self.myException, stack=False) del self.myException return e def openFolder(self, folderFunction='general', title='CCP4 Task Folder', toggle=[], toggleFunction=[], attributes={}, keywords={}, drawFolder=None,**kw): if self.subFrame is not None: self.closeSubFrame() self.closeFolder() self.currentFolder = CTaskFolder(self, folderFunction=folderFunction, title=title, toggle=toggle, toggleFunction=toggleFunction, attributes=attributes) self.currentFolderLayout = QtGui.QVBoxLayout() self.currentFolderLayout.setSpacing(CFolderTaskWidget.MARGIN) self.currentFolderLayout.setContentsMargins(CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN, CFolderTaskWidget.MARGIN) self.connect(self.currentFolder, QtCore.SIGNAL('folderToggled'), functools.partial(self.folderToggled, self.currentFolder)) if folderFunction == 'inputData': self.createJobTitle(followFrom=keywords.get('followFrom', True)) return self.currentFolder def closeFolder(self): if self.subFrame is not None: self.closeSubFrame() if self.tabWidget is not None: self.closeTabFrame() if self.currentFolder is not None: if self.currentFolder.folderFunction == 'inputData': self.createPatchFrame() self.currentFolderLayout.addStretch(1) self.currentFolder.setContentsLayout(self.currentFolderLayout) self.layout().addWidget(self.currentFolder) self.currentFolder = None self.currentFolderLayout = None def folderToggled(self, folder): self.layout().update() def getWidget(self, name): # findChild only finds stuff in top tab?? print "CCP4TaskManager.CFolderTaskWidgetgetWidget" return self.widgetLookup.get(name,[None])[0] def getFolderOpenStatus(self): status = [] for i in range(self.layout().count()): w = self.layout().itemAt(i).widget() #print 'getFolderOpenStatus',i,w if w is not None: status.append(w.isOpen()) return status def setFolderOpenStatus(self, openStatus): for i in range(min(len(openStatus), self.layout().count())): w = self.layout().itemAt(i).widget() if openStatus[i]: w.openFolder() else: w.closeFolder() #--------------------------------------------------------------------- class CToggle(QtCore.QObject): #--------------------------------------------------------------------- ERROR_CODES = {101 : {'description' : 'Can not set up toggle widget undefined for'}, 102 : {'description' : 'Can not set up toggle model undefined for'}, 103 : {'description' : 'Can not change visibility, widget undefined for'}, 104 : {'description' : 'Can not change visibility, model undefined for'}} def __init__(self, parent, target=None, parameter=None, state='open', values=[], parameterList=[], toggleFunction=None): QtCore.QObject.__init__(self, parent) #print 'CToggle.__init__',parameter,parent,target self.target = target self.parameter = parameter self.state = state self.values = values self.parameterList = [] self.parameterList.extend(parameterList) self.toggleFunction = toggleFunction self.connected = False def makeConnection(self): ''' widget = self.parent().findChild(QtGui.QWidget,self.parameter) #print 'CToggle.makeConnection',self.parameter,widget,widget.model if widget is None: raise CException(self.__class__,101,self.parameter) elif widget.model is None: raise CException(self.__class__,102,self.parameter) self.parent().connect(widget.model,QtCore.SIGNAL('dataChanged'),self.handleSignal) ''' container = self.parent().parentTaskWidget().container if container is None: return if self.parameter is not None: obj = container.find(self.parameter) if obj is None: return self.parent().connect(obj,QtCore.SIGNAL('dataChanged'),self.handleSignal) else: for param in self.parameterList: obj = container.find(param) if obj is not None: self.parent().connect(obj,QtCore.SIGNAL('dataChanged'),self.handleSignal) self.connected = True def handleSignal(self, **kw): if self.parameter is not None: self.setTargetVisibility() else: self.applyVisibilityFunction() def setTargetVisibility(self): ''' widget = self.parent().findChild(QtGui.QWidget,self.parameter) #print 'setTargetVisibility',self.parent(),self.parameter,widget if widget is None: raise CException(self.__class__,103,self.parameter) if widget.model is None: raise CException(self.__class__,104,self.parameter) ''' obj = self.parent().parentTaskWidget().container.find(self.parameter) if obj is None: return value = obj.get() #print 'CToggle.setTargetVisibility',self.parameter,value,self.values,self.values.count(value),self.state #if isinstance(self.target,CTaskLine) or isinstance(self.target,CSubFrame) or isinstance(self.target,CTaskFolder): if 1: if self.values.count(value): if self.state == 'open': self.target.show() else: self.target.hide() else: if self.state == 'open': self.target.hide() else: self.target.show() def getTargetVisibility(self): try: obj = self.parent().parentTaskWidget().container.find(self.parameter) except: return True if obj is None: return True value = obj.get() if 1: if self.values.count(value): return self.state == 'open' else: return self.state != 'open' def applyVisibilityFunction(self): vis = self.toggleFunction() #print 'applyVisibilityFunction',vis if vis: self.target.show() else: self.target.hide() #--------------------------------------------------------------------- class CTaskFolder(CCP4Widgets.CFolder): #--------------------------------------------------------------------- def __init__(self,parent=None, folderFunction='general', title='Folder', copen=1, toggle=[], toggleFunction=[], attributes={}): self.folderFunction = folderFunction if ['protocol'].count(folderFunction): titleBar = 0 else: titleBar = 1 #print 'CTaskFolder.__init__',title self._attributes = attributes CCP4Widgets.CFolder.__init__(self, parent, title, copen, titleBar, toggle=toggle) from qtgui import CCP4StyleSheet self.setTitleColour(CCP4StyleSheet.LOWLIGHTCOLOUR) if self.titleBar is not None: self.titleBar.setMaximumHeight(30) def setupUpdateStatus(self, *args): pass #--------------------------------------------------------------------- class CSubFrame(QtGui.QFrame): #--------------------------------------------------------------------- def __init__(self,parent): QtGui.QFrame.__init__(self,parent) #--------------------------------------------------------------------- class CTaskLine(QtGui.QFrame): #--------------------------------------------------------------------- ERROR_CODES = {101 : {'description' : 'No data container for task line'}, 102 : {'description' : 'No model found for task line item'}, 103 : {'description' : 'Error creating widget for task line item'}} MARGIN = 0 def __init__(self, parent=None): QtGui.QFrame.__init__(self, parent) self.setObjectName('taskLine') layout = QtGui.QHBoxLayout() layout.setSpacing(CTaskLine.MARGIN) layout.setContentsMargins(CTaskLine.MARGIN, CTaskLine.MARGIN, CTaskLine.MARGIN, CTaskLine.MARGIN) self.setLayout(layout) self.tip = None self.helpTarget = '' def parentTaskWidget(self): return self.parent().parentTaskWidget() def addWidget(self, widget): lastItem = self.layout().itemAt(self.layout().count()-1) #print 'CTaskLine.addWidget lastItem',lastItem if lastItem is not None and isinstance(lastItem, QtGui.QSpacerItem): self.layout().insertWidget(self.layout().count()-1,widget) else: self.layout().addWidget(widget) def draw(self, definition=[], dataContainer=None, setupFolder=None, attributes={}): #print 'CTaskLine.draw',definition doneStretch = False if dataContainer is None: raise CException(self.__class__, 101) myException = CErrorReport() widgetQualifiers = {} widgetQualifiers.update(attributes) #print 'CTaskLine.draw widgetQualifiers',widgetQualifiers pDef = -1 definition.append('') ifFullLine = False # KJS : This loop needs looked at. Investigate later. while (pDef < len(definition) - 2): widget = None pDef = pDef + 1 if definition[pDef] in ['message', 'tip']: pDef = pDef + 1 self.tip = definition[pDef] if definition[pDef] == 'help': pDef = pDef + 1 self.helpTarget = definition[pDef] if definition[pDef] == 'label': pDef = pDef + 1 #FIXME - SJM 30/05/2017 - I have no idea why the colour is hardwired. It means that setEnabled(False) shows no "greying out" effect. #lab = QtGui.QLabel('' + definition[pDef] + '') lab = QtGui.QLabel(''+definition[pDef]+'') if self.tip is not None: lab.setToolTip(self.tip) self.layout().addWidget(lab) if definition[pDef] == 'subtitle': pDef = pDef + 1 subtitle = QtGui.QLabel(definition[pDef]) pDef = pDef + 1 subtitle.setToolTip ( '' + definition[pDef] + '' ) subtitle.setObjectName ( 'subtitle' ) self.layout().addWidget ( subtitle ) if self.tip is not None: subtitle.setToolTip(self.tip) if definition[pDef] in [ 'advice','warning']: pDef = pDef + 1 #label = QtGui.QLabel('' + definition[pDef] + '') label = QtGui.QLabel(''+definition[pDef]+'') label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) self.layout().setContentsMargins(CFolderTaskWidget.MARGIN,CFolderTaskWidget.MARGIN,CFolderTaskWidget.MARGIN,CFolderTaskWidget.MARGIN) self.layout().setSpacing(CFolderTaskWidget.MARGIN) if definition[pDef-1] == 'advice': label.setObjectName('italic') self.layout().addWidget(label) else: label.setObjectName('warning') label.setWordWrap(True) self.layout().addWidget(label) if self.tip is not None: lab.setToolTip(self.tip) if definition[pDef] == 'spacing': pDef = pDef + 1 self.layout().addSpacing( definition[pDef]) ifFullLine = True if definition[pDef] == 'stretch': self.layout().addStretch( 1) ifFullLine = True if definition[pDef] == 'launchButton': pDef = pDef + 1 taskName = definition[pDef] widget = QtGui.QPushButton(TASKMANAGER().getTaskAttribute(taskName,'TASKTITLE')) widget.setObjectName(taskName) if self.tip is not None: widget.setToolTip(self.tip) self.connect(widget,QtCore.SIGNAL('released()'),functools.partial(self.parentTaskWidget().emit,QtCore.SIGNAL('launchJobRequest'),taskName,{'launchJobId':self.parentTaskWidget().jobId()})) self.layout().addWidget(widget) if definition[pDef] == 'widget': pDef = pDef + 1 while definition[pDef][0] == '-': widgetQualifiers[definition[pDef][1:]] = definition[pDef+1] pDef = pDef + 2 model = dataContainer.find(definition[pDef]) #print 'CTaskLine.draw',model,definition[pDef] #if len(widgetQualifiers)>0: print 'CTaskLine.draw widgetQualifiers',definition[pDef],widgetQualifiers if model is None: myException.append(self.__class__, 102, definition[pDef], stack=False) else: if not (self.parent().parent().paramsList.count(definition[pDef])): self.parent().parent().paramsList.append(definition[pDef]) widget = None qualifiers = {} qualifiers.update(model.qualifiers()) qualifiers.update(widgetQualifiers) #print 'widgetQualifiers',definition[pDef],model.qualifiers(),widgetQualifiers if DEVELOPER(): widget = DATAMANAGER().widget(model=model, parentWidget=self.parent(), qualifiers=widgetQualifiers, name=definition[pDef]) else: try: widget = DATAMANAGER().widget(model=model, parentWidget=self.parent(), qualifiers=widgetQualifiers, name=definition[pDef]) except CException as e: e.appendDetails(definition[pDef]) myException.extend(e) except: myException.append(self.__class__, 103, definition[pDef]) #print 'CTaskLine widget',definition[pDef],widget,widget.STRETCH #widget = dataContainer.widget(name=par,parentWidget=self,widgetQualifiers=widgetQualifiers) if widget is not None: widget.setObjectName(definition[pDef]) #modelTip = model.qualifiers('toolTip') #if modelTip is not NotImplemented: # widget.setToolTip(modelTip) if self.tip is not None: widget.setToolTip(self.tip + ' (' + str(definition[pDef]) + ')') else: tip = model.qualifiers('toolTip') if tip is not NotImplemented and tip is not None: widget.setToolTip(model.qualifiers('toolTip') + ' (' + str(definition[pDef]) + ')') else: widget.setToolTip(str(definition[pDef])) self.layout().addWidget(widget) if widget.STRETCH > 0: #print 'CTaskLine.draw stretching',model.objectName() self.layout().setStretch(self.layout().count() - 1, widget.STRETCH) doneStretch = True elif widgetQualifiers.get('charWidth', 1) < 0: doneStretch = True tW = self.parentTaskWidget() # KJS: Change to functools version self.connect(widget, QtCore.SIGNAL('contextMenuRequest'), functools.partial(tW.populateContextMenu, definition[pDef], tW.programHelpFile, self.helpTarget)) if not tW.widgetLookup.has_key(definition[pDef]): tW.widgetLookup[definition[pDef]] = [] tW.widgetLookup[definition[pDef]].append(widget) if setupFolder is not None: setupFolder(model) if isinstance(widget,CCP4Widgets.CComplexLineWidget): ifFullLine = True if definition[pDef] == 'format': pass ''' if ['toggle','toggle_display'].count(definition[pDef]): if pDef+3 0: self.myException.report() self.currentFolder = None self.currentFolderLayout = None self.parentTaskWidget().updateViewFromModel() self.applyToggles() def parentTaskWidget(self): return self.parent() def draw(self): self.myException = CErrorReport() self.parentTaskWidget().drawContents() self.parentTaskWidget().updateViewFromModel() self.closeFolder() self.parentTaskWidget().setDefaultParams() self.applyToggles() e = CErrorReport() #print 'CTaskWidget.draw',len(e) e.extend(self.myException, stack=False) del self.myException return e def openFolder(self, folderFunction='general', title='CCP4 Task Folder', toggle=[], toggleFunction=[], attributes={}, keywords={}, drawFolder=None, **kw): if self.subFrame is not None: self.closeSubFrame() self.closeFolder() self.currentFolder = CTabFrame(self, folderFunction=folderFunction, title=title, attributes=attributes, drawFolder=drawFolder) if len(toggle) > 0: if len(toggle) == 1: self.setToggle(self.currentFolder, toggle[0], 'open', [True]) elif len(toggle) == 2: self.setToggle(self.currentFolder, toggle[0], toggle[1], [True]) else: self.setToggle(self.currentFolder, toggle[0], toggle[1], toggle[2]) if len(toggleFunction) > 0: self.setToggle(self.currentFolder, toggleFunction=toggle[0], parameterList=toggle[1]) self.currentFolderLayout = QtGui.QVBoxLayout() self.currentFolderLayout.setSpacing(MARGIN) self.currentFolderLayout.setContentsMargins(MARGIN, MARGIN, MARGIN, MARGIN) self.currentFolder.frame.setLayout(self.currentFolderLayout) if folderFunction == 'inputData': self.createJobTitle(followFrom=keywords.get('followFrom', True)) return self.currentFolder def closeFolder(self): if self.subFrame is not None: self.closeSubFrame() if self.tabWidget is not None: self.closeTabFrame() if self.currentFolder is not None: if self.currentFolder.folderFunction == 'inputData': self.createPatchFrame() self.currentFolderLayout.addStretch(1) self.currentFolder.frame.show() #self.currentFolder.setLayout(self.currentFolderLayout) self.addTab(self.currentFolder,self.currentFolder.title) self.setTabToolTip(self.count()-1,self.currentFolder.title) if ['inputData'].count(self.currentFolder.folderFunction) != 0: self.currentFolder.updateStatus() self.currentFolder = None self.currentFolderLayout = None def getWidget(self,name): # findChild only finds stuff in top tab?? return self.parent().getWidget(name) def validate(self): # This is probably broken!! totInvalid = 0 for i in range(self.count()): frame = self.widget(i) nInvalid = 0 widgetList = frame.findChildren(CCP4Widgets.CViewWidget) #print 'CTabFolder.validate widgetList',widgetList for widget in widgetList: if widget.validate() is not None: nInvalid + widget.validate() #print 'CTabFolder.validate nInvalid',nInvalid if nInvalid > 0: print 'INVALID FRAME' frame.setTabColour('red') totInvalid = totInvalid + nInvalid else: frame.setTabColour('black') return totInvalid def resetJobCombos(self): #print 'CTabTaskWidget.resetJobCombos' for i in range(self.count()): frame = self.widget(i) widgetList = frame.findChildren(CCP4Widgets.CViewWidget) for widget in widgetList: if isinstance(widget, CCP4Widgets.CDataFileView): widget.loadJobCombo() widget.updateJobCombo() widget.updateModelFromView() #--------------------------------------------------------------------- class CTabFrame(QtGui.QFrame): #--------------------------------------------------------------------- def __init__(self, parent=None, folderFunction='general', title='Folder', attributes={}, drawFolder=None): QtGui.QFrame.__init__(self, parent) self.setObjectName(title) self._drawFolder = drawFolder self.title = title self.folderFunction = folderFunction self._attributes = attributes self.dataObjects = {} self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) self.scrollArea= QtGui.QScrollArea(self) self.layout().addWidget(self.scrollArea) self.frame = QtGui.QFrame() self.scrollArea.setWidget(self.frame) self.scrollArea.setWidgetResizable(1) self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) def hide(self): tab = self.parent().parent() indx = tab.indexOf(self) if tab.currentIndex() == indx: tab.setCurrentIndex(0) tab.setTabEnabled(indx, False) def show(self): tab = self.parent().parent() indx = tab.indexOf(self) tab.setTabEnabled(indx, True) def setTabColour(self,colour): tab = self.parent().parent() indx = tab.indexOf(self) tab.tabBar().setTabTextColor(indx, QtGui.QColor(colour)) def updateStatus(self, dataObjectName=None): #print 'CTabFrame.updateStatus',dataObjectName allSet = True for key, obj in self.dataObjects.items(): #print 'CTabFrame.updateStatus testing',key,obj.validity().report() if obj.validity(obj.get()).maxSeverity() > SEVERITY_WARNING: allSet = False break if allSet: self.setTabColour('black') else: self.setTabColour('red') #--------------------------------------------------------------------- class CStackedWidget(QtGui.QStackedWidget): #--------------------------------------------------------------------- # This did not work - nothing displayed despite all diagnostic output correct def __init__(self, parent=None, controlVar=None): self.controlVar = controlVar QtGui.QStackedWidget.__init__(self, parent) controlWidget = self.parent().getWidget(self.controlVar) #print 'CStackedWidget.__init__',controlWidget.model if controlWidget is not None: self.connect(controlWidget.model, QtCore.SIGNAL('dataChanged'), self.update) def parentTaskWidget(self): parent = self.parent() while 1: if isinstance(parent, CTaskWidget): return parent if isinstance(parent, QtGui.QMainWindow): return None parent=parent.parent() def update(self): # I'd not expected to have to go up so far - has Qt reparented? controlWidget = self.parentTaskWidget().getWidget(self.controlVar) #print 'CStackedWidget.update',self.controlVar,controlWidget if controlWidget is None: return value = controlWidget.model.get() valueList = controlWidget.model.qualifiers('enumerators') #print 'CStackedWidget.update',value,valueList if value in valueList: indx = valueList.index(value) #print 'CStackedWidget.update',value,indx self.setCurrentIndex(indx) #--------------------------------------------------------------------- class CStackedLayout(QtGui.QStackedLayout): #--------------------------------------------------------------------- def __init__(self, parent=None, controlVar=None): self.controlVar = controlVar QtGui.QStackedLayout.__init__(self) controlWidget = parent.getWidget(self.controlVar) #print 'CStackedLayout.__init__',controlWidget.model if controlWidget is not None: self.connect(controlWidget.model, QtCore.SIGNAL('dataChanged'), self.update) def parentTaskWidget(self): parent = self.parentWidget() while 1: if isinstance(parent, CTaskWidget): return parent if isinstance(parent, QtGui.QMainWindow): return None parent = parent.parent() def update(self): # I'd not expected to have to go up so far - has Qt reparented? #print 'CStackedLayout.update',self.parentWidget() controlWidget = self.parentTaskWidget().getWidget(self.controlVar) #print 'CStackedLayout.update',self.controlVar,controlWidget if controlWidget is None: return value = controlWidget.model.get() valueList = controlWidget.model.qualifiers('enumerators') #print 'CStackedWidget.update',value,valueList if value in valueList: indx = valueList.index(value) #print 'CStackedLayout.update',value,indx self.setCurrentIndex(indx) #=========================================================================================================== import unittest def TESTSUITE(): suite = unittest.defaultTestLoader.loadTestsFromTestCase(testTaskWidget) return suite def runAllTests(): suite = TESTSUITE() unittest.TextTestRunner(verbosity=2).run(suite) #------------------------------------------------------------------- class CSummatTask(CFolderTaskWidget): #------------------------------------------------------------------- # Subclass CTaskWidget to give specific task window def __init__(self, parent): CTaskWidget.__init__(self, parent=None) def drawContents(self): #self.setProgramHelpFile('fft') #self.openFolder(folderFunction='protocol') self.openFolder(title='Test folder') #self.createTitleLine() self.createLine(['widget', 'nCycles', 'label', 'cycles with cutoff', 'widget', 'cutoff']) self.createLine(['label', 'Range of gubbins', 'widget', 'range' ] ) #class testTaskWidget(unittest.TestCase): class testTaskWidget(): def __init__(self): self.setUp() def setUp(self): from core.CCP4Modules import QTAPPLICATION self.app = QTAPPLICATION() self.window = QtGui.QMainWindow() def test1(self): from core.CCP4Container import CContainer summat = CContainer(parent=self.app, definitionFile='/Users/lizp/Desktop/dev/ccp4i2/sandpit/summat.contents.xml') self.assertEqual(len(summat.CONTENTS), 4, 'Container - Wrong content length') self.assertEqual(summat.gubbins.startValue, 12.0, 'Container - sub-container CFloat wrong initial value') def test2(self): from core.CCP4Container import CContainer import sys self.container = CContainer(parent=self.app, definitionFile='/Users/lizp/Desktop/dev/ccp4i2/sandpit/summat.contents.xml') self.task = CSummatTask(self.window) self.task.setContainer(self.container) self.task.draw() self.window.setCentralWidget(self.task) self.window.show() sys.exit(self.app.exec_()) def tearDown(self): self.app.quit()