CCP4I2 Developers: Writing Task GUIs

Introduction
Folders/tabs
CTaskWidget class overview
createLine() definition keywords
toggle and toggleFunction arguments
Accessing individual widgets
Widget Qualifers
Using the Qt Signals and Slots
Data analysis in the task gui
Changing parameter qualifiers
Auto-generating GUIs
Multiple Task Interfaces to One Wrapper
The Task Menu
Creating Child Pop-out Task GUI
Speeding drawing of tasks

Introduction

Please read the Data Classes documentation first.

If you have implemented a wrapper or pipeline in the ccp4i2 wrappers or pipelines directories then a basic gui can be autogenerated from the def file and will be accessible from the 'Wrappers' section at the bottom of the list of jobs. Note that if the wrapper uses some data object that does not have a corresponding widget implelemented then that data will not appear in the gui and you need to ask Liz to fix that. To create a better gui you need to implement a Python script called tasks/mytask/CTaskmytask.py as a subclass of CTaskWidget from qtgui/CCP4TaskWidget. See < a href="../../tasks/buccaneer_build_refine/CTaskbuccaneer_build_refine.py">tasks/buccaneer_build_refine/CTaskbuccaneer_build_refine.py for an example.Also look at < a href="../../tasks/demo/CTaskdemo.py">tasks/demo/CTaskdemo.py does not interface to any real wrapper but is used to test and demo gui features.

Note that if there is an error in the Python file which prevents it being imported when CCP4i2 starts then this is reported to the terminal and the Program print log (under Utilities pull-down menu) and the task will not appear on the task menu. Any other errors will be reported to the same places when you try to open the task widget.

Folders/Tabs

The task gui is split into 'folders' or 'tabs'; these two modes depend on the option under Preferences but do not require any alternative coding by the application programmer. The top folder is Input Data which should contain all of the required input files or data. Ideally a user should be able to run the program without looking at any other folder. There are currently a couple of features in the Input Data folder that are added by the core system:

  • an option to set the job title
  • a 'follow from' option to choose a previous job whose ouput data will be input to this job
  • The 'follow from' features should be switched off ( CTaskWidget.AUTOPOPULATEINPUT = False ) if the the task is only importing data. Note that the application should not have any jobTitle parameter that would be redundant with the jobTitle parameter handled by the core. The parameters managed by the core are recorded in the task container in a sub-container named guiParams and appear at the bottom of the params files.

    The 'follow from' feature is implemented by the core system but, for the appropriate input files, the qualifier fromPreviousJob must be set True. There is no 'Output Data' folder - the output file names are set automatically and the user can 'export' files after the job has completed. Also there is no 'Protocol' folder (as ther was in CCP4i) so some options that may have gone in the 'Protocol' folder may now be in the 'Input data' folder. There is usually an Options folder for basic options and then there may be one of more specific advanced options folders.

    Note that the initial opening of a complex task in the gui can take significant time and a way to speed this up is to only draw the 'hidden' task frames when the user opens them - see speedup below.

    CTaskWidget class overview

    Minimal code to create a new task widget:

    import CCP4TaskWidget
    class CTaskBuccaneer(CCP4TaskWidget.CTaskWidget):
      TASKNAME = 'buccaneer'
      TASKVERSION = 0.0
      TASKMODULE='model_building'
      TASKTITLE='Buccaneer automated model building'
      SHORTTASKTITLE='Buccaneer'
      WHATNEXT = ['coot_rebuild','refmac_martin','buccaneer_build_refine']
      MGDISPLAYFILES = ['XYZOUT']
      DESCRIPTION = '''Do several cycles of alternate model building with Buccaneer and refinement with Refmac'''
      
      def __init__(self,parent):
        CCP4TaskWidget.CTaskWidget.__init__(self,parent)
    
      def drawContents(self):
        pass
    

    The essential attributes for the sub-class

  • TASKNAME - the task name that should be the same as the TASKNAME in the corresponding wrapper
  • TASKVERSION - the versioning is not currently used but should correspond to the wrapper version How should it map to CCP4 and program versions??
  • TASKMODULE - the catagory that the task appears in on the gui task menu. Must be one of those listed in MODULE_ORDER in qtcore/CCP4TaskManager.py. The wrapper and test modules will not appear in a release version of the program.
  • TASKTITLE - title to appear in the gui task menu.
  • SHORTTASKTITLE - short title to appear in the gui where brevity necessary e.g. 'What next'and JobList .
  • DESCRIPTION - a longer description to appear as a tooltip in the gui task menu.
  • MGDISPLAYFILES - a list of the parameters containing the names of files to be displayed in molecular graphics for this job
  • WHATNEXT - a list of tasks that may follow after this task (or implement whatNext() function - see below).
  • PROGRAMHELP - a list of program help files found as $CCP4/html/xxxx.html to be listed under Help button
  • Other possible attributes:

  • CLONEABLE - Boolean flag if the task can be cloned - default is True
  • EDITOR - is the task solely for data input (default False). If True the gui is modified to give a 'Save' button rather than a 'Run' button.
  • AUTOPOPULATEINPUT - can the inputs to this task be auto-populated from a previous task (default True)
  • The drawContents() method should specify the layout of the gui using the library of methods documented below.

    CTaskWidget methodArgumentDescription
    openFolderStart definition of a tab or folder
    folderFunctionDefault is 'general' - should be 'inputData' for first folder
    titleA short title to appear on folder tab or title bar
    toggleA list of parameters for controlling visibility of the folder - see below
    toggleFunctionA list of parameters for controlling visibility of the folder - see below
    closeFolderEnd definition of a tab or folder
    createLineDefine content on one line of the gui - returns a Qt QFrame
    defintionlist of strings that are keywords and parameters to define line content (see below)
    toggleA list of parameters for controlling visibility of the line - see below
    toggleFunctionA list of parameters for controlling visibility of the line - see below
    appendLineA preceeding line (as returned by createLine()) to which this 'line' is appended
    openSubFrameStart definition of lines that are grouped for purpose of toggling visibility or creating a frame. Returns a Qt QFrame.
    framedefault False - if true draw a frame around the sub-frame
    toggle A list of parameters for controlling visibility of the sub-frame - see below
    toggleFunctionA list of parameters for controlling visibility of the folder - see below
    closeSubFrameEnd definition of group pf lines
    openStackOpen definition of a stack of two or more lines of which only one will be displayed
    controlVarName of variable controlling which line in the stack is visible. This variable is probable presented as radio buttons in the preceeding line of the gui.
    closeStackEnd definition of a stack
    createRadioGroupCreate set of radio buttons aranged one per line
    labelA string label
    itemListA list of string labels for each radio button
    objectNameThe name of the parameter that is controlled by the radio buttons - expected to have enumerators qualifer that is list same length as itemList
    setHelpFileName of help file linked to by all widgets defined after a call to this method
    helpFileThe name of a file that is expected to be found in $CCP4_TOP/docs/tasks
    whatNextThis is a module level function that returns a list of suggested next tasks.
    jobIdThe id of a finished job that is an instance of this task

    The following methods may be needed to define some more dynamic behaviour.

    CTaskWidget methodArgumentDescription
    visibleFolderReturn the index of the curently visible 'folder' - only relevant in tab mode.
    setVisibleFolderSet which 'folder' is visible (only relevant in tab mode)
    titletitle of 'folder'
    indexindex of folder (only one of these arments should be used)
    getContainerReturn the instance of CContainer which holds the task parameters
    getWidgetReturn the widget for the named parameter
    nameParameter name
    projectReturn the name of the project of the job open in the task window
    jobIdReturn the id of the job (an integer) of the job open in the task window
    getParamsReturn python dict of values of parameters currently set in the gui
    paramsValuesA Python dict whose keys are the names of the parameters whose values are required.
    setParamsSet gui widget values to reflect the input parameters
    paramsValues A Python dict of values to be set in the gui
    updateViewFromModelrefresh the gui from the parameters in the task container
    validatevalidate and mark any invalid parameters in the gui
    isValidReturn a list of any invalid parameters in the gui
    createWidgetReturn an instance of the appropriate widget for a named parameter in the task container
    nameParameter name
    widgetQualifiersa Python dict of qualifying parameters for the widget

    createLine() definition keywords

    The definition argument to createLine() is a list of strings which consist of keywords followed by a fixed number of arguments. Note that a 'line' may be a very fat line containing a complex widget - in this case there should probably be no other labels or widgets specified in the line.

    keywordArgumentDescription
    helpA help link applied to all following widgets in the line
    targetThe target part of the link - complements the file specified by setHelpFile()
    tipTooltip to apply to all following widgets in the line
    tipA short text string
    messageAlternative keyword for 'tip'
    labelA text label to appear in the line. Can be styled using HTML tags.
    labela text string
    adviceA text label to appear in italics in the line. Can be styled using HTML tags. There should not be anything else in the line
    labela text string
    subtitleA differently-coloured label that can be used to define sections. Adds a vertical spacer before it and shows a tooltip that should be used to explainthe forthcoming section. Can be styled using HTML tags. There should not be anything else in the line
    labela text string for the label
    tooltipa text string for the tooltip
    spacingAdd a non-stretchable space
    spacingNumber of pixels
    stretchAdd stretchable space (usually end of line)
    stretch factorinteger (default =1 for widgets and labels) larger value means more stretchable
    widgetAdd widget to the line
    -qualifierNote '-' first character. Qualifier passed as argument to the widget initialisation method
    nameName of parameter

    toggle and toggleFunction arguments

    These are arguments to createLine(), openFolder() and openSubFrame() control whether the line or frame is visible. The visibility is usually dependent on a parameter in the task container (for the toggle argument) or, more complex cases (handled by toggleFunction> have a dependency on multiple parameters and require a function to be implemented to evaluate and return a logical value. If controlling the gui appearance requires the definition of some parameters that are not subsequently used in program wrappers then they should be put in the guiParameters container in the task definition file.

    The toggle argument is a list of 3 parameters:

  • parameter - the name of a parameter in the task container
  • state - the state of the line or folder i.e. 'open' or 'closed' if the parameter has the values..
  • values - a list of values - default is [True]
  • The toggleFunction argument is a list of 2 parameters:

  • function - a method returning True if the line should be visible
  • dependencies - parameters on which the visibility is dependent - it will be updated if any of these parameters change
  • Accessing individual widgets

    It is sometimes necessary to access the individual widgets defined by createLine() - this can be done by callying getWidget(name) where name is the name of the paramter. This returns a Qt widget that can be customised using the standard Qt methods for that widget type (see the Qt documentation).

    Widget Qualifers

    These are arguments to createLine() that are inserted after the keyword widget and before the name of the parameter that the widget will represent. For each qualifier the name of the qualifier and the qualifier value are inserted in the argument list - for example:

          self.createLine( [ 'widget', '-browseDb', True, 'ABCD' ] )
    

    The most common qualifiers are:

    QualifierWidget for data typeDescription
    -guiLabelCDataFileA short text label to appear on the widget
    -browseDbCDataFileA boolean value. Control display of icon to browse the database for files.
    -guiModeCString,CInt,CFloat which have enumerator Values 'radio','multiLineRadio' or 'combo' determine presentation of alternative parameter values
    -titleCListA short title to appear at top of a list/tree widget

    Using the Qt Signals and Slots

    The main reason to use signals and slots is to cause some code to be run when the user provides some input. An example of such code might be resetting defaults dependent on the user's choice of input file. Qt signals and slots are explained in detail here but you only need to know that any Qt object (and, in the context of the gui, both the data objects and the widgets are Qt objects) emits a signal when something happens (e.g. a button is clicked or the value of a data object is changed). Other objects can 'connect' to the signal i.e. request that whenever a particular signal is emited by a particular object then a particular function is run. The most relevant signal in CCP4i2 is the dataChanged signal from any data object whenever it is changed.

    The code that you require to run on receiving a signal must be a separate function, almost certainly a method of your CTaskWidget subclass. The request to call that function is via the connect() method and should probably go at the end of task widget drawContents() method. For example to call the analyseXyzin() method every time the XYZIN parameter is changed:

      from PyQt4 import QtCore
    
      ...
      def drawContents(self):
        self.createLine( [  'label','Input model ','widget','XYZIN' ] )
    
        ...
    
        self.connect(self.container.inputData.XYZIN,QtCore.SIGNAL('dataChanged'),self.analyseXyzin)
        self.analyseXyzin()
    
      def analyseXyzin(self):
        if not self.container.inputData.XYZIN.isSet() or not self.container.inputData.XYZIN.exists(): return
        ...
    

    Beware that the dataChanged signal is also emited when the parameter has been unset so you need to test carefully (as in the example above) that the value is valid and may need to unset defaults (or whatever your function does). Beware also that you may need to call your function explicitly once when you draw the gui to get things setup initially with whatever data is initially set.

    Note that there is an alternative to calling connect - instead use CPluginScript.connectDataChanged() :

        self.connectDataChanged('XYZIN',self.analyseXyzin)
    

    which is simpler but only applies to dataChanged signals.

    Text input widgets can be tricky because the dataChanged signal will be emitted for every character that the user types and the unfinished input is probably invalid. An alternative approach for text input is to use the editingFinished() signal from the actual text input widget - this is emitted when th user clicks return or moves the focus out of the widget so suggesting that the input is complete. An example of this calling setDefaultParameters() every time the user finishes editing the TEXT parameter:

      def  drawContents(self):
        self.createLine( [  'label','Input some text ','widget','TEXT' ] )
    
        '''
        self.connect( self.getWidget('TEXT').widget, QtCore.SIGNAL('editingFinished()'), self.setDefaultParameters )
    
    
      def setDefaultParameters(self):
        ....
    

    The getWidget() method is used to get the widget for the TEXT parameter but note that the actual object connected to is the member widget. Also note the brackets that are essential in the editingFinished() signal definition.

    Data analysis in the task gui

    The gui code may need to do significant analysis in order to check for appropriate input or set appropriate default parameters etc.. There is an example of this in the Matthews task where the results of an analysis are presented in a text widget.

    It is possible to use wrapper scripts from the gui as demonstrated in the Crank2 task. The significant code there is:

      import CCP4Modules,CCP4PluginScript
      try:
        workDir = CCP4Modules.PROJECTSMANAGER().jobDirectory(self.jobId(),subDir='TMP')
      except:
        workDir = None
      try:
        defaults = CCP4PluginScript.CPluginScript(dummy=True,workDirectory=workDir).makePluginObject(pluginName='crank2',reportToDatabase=False).process(self.container)
      except:
        pass
    

    First this code calls PROJECTSMANAGER().jobDirectory() to get the path of the TMP sub-directory of the appropriate job directory so all files will be written here. (The TMP sub-directory may be deleted later by Cleanup.)Then an instance of the CPluginScript base class is created and the makePluginObject() method used to create an instance of the required 'crank2' wrapper. The CPluginScript is instantiated with dummy=True that prevents jobs and files being recorded in the database and the workDirectory is set to the TMP sub-directory. Sub-tasks of this instance of CPluginScript, such as the crank2 plugin, are then given job-directories in the TMP directory. The crank2.process() method has an optional container argument that is passed the current gui container with the current state of the gui that can be queried by the crank2.process() method.

    Validation and changing parameter qualifiers

    The validation of gui input to highlight missing/invalid input and to check before running a job is done by the widget's validate() method. The particular requirements for each data object are specified by qualifiers of the object that are usually defined in the def file. The requirements for some parameter may be dependent on other parameters such that its qualifiers mat need changing by using the CData.setQualifiers() method. There is an example of this in the dumMee demo task ( tasks/dummy/CTaskDummy.py can be seen if the Demo folders on the task menu are open). The behaviour of the PDBIN file parameter is dependent on the PDBIN_COMPULSARY boolean parameter.

        self.createLine ( [ 'widget' , 'PDBIN_COMPULSARY' , 'label' ,'Model data input is compulsary'])
        self.createLine ( [ 'widget','PDBIN' ] )
    
        self.connectDataChanged('PDBIN_COMPULSARY',self.handlePDBIN_COMPULSARY)
        self.handlePDBIN_COMPULSARY()
    

    Here the two widgets for PDBIN_COMPULSARY and PDBIN are drawn and then a connectDataChanged call ensures that handlePDBIN_COMPULSARY() is called whenever PDBIN_COMPULSARY is changed. There is also an call to handlePDBIN_COMPULSARY() to initialise the status correctly. The code in handlePDBIN_COMPULSARY() checks the state of PDBIN_COMPULSARY and sets the allowUndefined qualifier of PDBIN appropriately. There is also a call to the validate() method of the PDBIN widget to ensure the highlightling of the widget is updated.

      def handlePDBIN_COMPULSARY(self):
        mode = bool(self.container.inputData.PDBIN_COMPULSARY)
        self.container.inputData.PDBIN.setQualifiers({'allowUndefined' : (not mode) } )
        self.getWidget('PDBIN').validate()
    

    The behaviour seen in the gui is that if the PDBIN_COMPULSARY checkbox is checked but there is no valid file selected in the PDBIN widget then that widget is highlighted.

    When the user clicks the Run button the input is validated and if the validation fails there is an error report and the job run is aborted. By default the tests are the same validation checks of individual widgets that lead to widgets being highlighted in red. If the task requires additional tests, perhaps cross-checking two or more separate widgets, then you should implement a CTaskWidget.taskValidation() method that returns a CErrorReport. There is an example of this at the bottom of dumMee demo task ( tasks/dummy/CTaskDummy.py can be seen if the Demo folders on the task menu are open). The example tests if the space group in PDB and MTZ file are the same and returns an error if they are not.

    Auto-generating GUIs

    This is a work-in-progress (June 2014) being developed particularly for generating a gui for MR Phaser. Other interfaces might require additional features - please contact Liz.

    'Autogenerating' means creating a GUI from the information in the def file without any Python coding. When CCP4i2 is run in DEVELOPER mode it will autogenerate guis from def files for any wrappers which do not have a specified gui. The same mechanism, generating gui elements from information in the def file, can be used for some parts of a gui, this is probably most useful for the more obscure (and perhaps more changeable) control parameters. We expect that most control parameters are simple data types (booleans,floats etc) that have widgets defined in CCP4i2 - more complex data types might need custom written widgets and guis. An example of including some autogenerated gui is in the drawContents() method of dumMee demo task ( tasks/dummy/CTaskDummy.py can be seen if the Demo folders on the task menu are open). The Autogenerated folder is generated by the code:

     self.openFolder(title='Autogenerated')
     self.autoGenerate(container=self.container.controlParameters,selection={'includeParameters' : ['OBSCURE_MODE','OBSCURE_CUTOFF']})
    

    After opening a folder the autoGenerate() method is called with the arguments: container that is the def file container that is the source of specifications of parameters and selection that is a Python dict with several possible keys that specify which parameters in the container are to be drawn.The includeParameters item lists parameters to be included in the gui or excludeParameters lists parameters to be excluded when putting all other parameters from the container in the gui.

    All data classes have three optional qualifiers that help define presentation in an autogenerated gui:
    QualifierDescription
    -guiLabelA short text label to appear by the widget
    -toolTipA longer text description
    -guiDefinitionThe xml element contains sub-elements that will be converted to a Python dict. There is no restriction on key values but currently used key values are given below

    guiDefinition keysDescription
    toggleParameterName of a parameter that controls visibility of the widget in the GUI
    toggleStateThe state (open/closed) for the listed toggleValues (default is open)
    toggleValuesThe values of toggleParameter for which toggleState applies
    expertLevel(Or any arbitary name) can be used in the selection argument to autoGenerate()

    Related parameters can be grouped into a container and they will then be displayed in a frame probably with a surrounding border. The visiblity of the frame will also be toggleable. The container can have the same qualifiers as listed above for the parameters.

    Multiple Task Interfaces to One Wrapper

    Some programs provide multiple functionalities that are used to solve different problems at different points in the structure solution process and these are best presented to the user in different task interfaces. To implement this the developer could provide multiple wrappers to the one program but one wrapper will be easier to maintain. Typically the different modes of the program may require different data file inputs and have different defaults. The input data, defaults etc. are set in def.xml files and again, to ease maintenance, it would be better to avoid replication between def.xml files. The solution to this is to have one 'base' def.xml file that specifies all the input, output and control parameters for the wrapper. A 'derived' def.xml file 'includes' the 'base' def.xml file and then specifies any altered parameter definitions that will overwrite the 'base' definitions. An example of this is in ccp4i2/tasks/molrep_den/molrep_den.def.xml

       <ccp4i2_header>
        ...
        ...
        <pluginName>molrep_den</pluginName>
        <pluginTitle>Fitting to a map using Molrep</pluginTitle>
        <jobId/>
      </ccp4i2_header>
      <ccp4i2_body id="molrep_den">
        <file>
          <CI2XmlDataFile>
            <project>CCP4I2_TOP</project>
            <relPath>wrappers/molrep_mr/script</relPath>
            <baseName>molrep_mr.def.xml</baseName>
          </CI2XmlDataFile>
        </file>
        <container id="inputData">
           <content id="F_SIGF">
             <className>CObsDataFile </className>
             <qualifiers>
       ...
       ...
    

    Here the molrep_den def file is 'derived' from the molrep_mr def file. The file/CI2XmlDataFile element specifies the file path for a 'base' def.xml file and the rest of this file contains specification of only a few parameters that are different between molrep_den and molrep_mr.

    The molrep_den GUI definition in ccp4i2/tasks/molrep_den/Cmolrep_den.py must also contain an additional class parameter GUINAME:

    class Cmolrep_den(CCP4TaskWidget.CTaskWidget):
    
      TASKTITLE='Search density using Molrep'
      TASKNAME = 'molrep_mr'
      GUINAME = 'molrep_den'
      TASKMODULE='model_building'
      ...
    

    Note that the TASKNAME is the name of the script that this GUI will run and GUINAME is the unique name for this GUI which should matchup with the pluginName in the header of the def.xml file. (In case you are wondering, for most GUIs the GUINAME is assumed to be the same as the TASKNAME.)

    The Task Menu

    The task menu in the gui is populated automatically at startup by taking information from the CTaskWidget subclasses. The modules presented in the menu are listed at the top of ccp4i2/core/CCP4TaskManager.py. The CTaskWidget class attributes that determine the menu are:

    CTaskWidget Class AttributesDescription
    TASKNAMEThe obligatory name of the script essential for cross-refererence to other files
    TASKVERSIONA version number for the script. Please give one!
    TASKMODULERefers to where the task should appear in the task menu, can be list of multiple modules
    TASKTITLEThe name for the task to appear in the GUI
    DESCRIPTIONMore details to appear in the GUI

    Note that tasks under developement should be put in the wrapper or test module and the developer can make these modules visible in the gui by setting the SHOW_WRAPPERS parameter at the top of the ccp4i2/core/CCP4TaskManager.py file to True. Note that the demo task dumMee is only visible if SHOW_WRAPPERS is True.

    The preferred style for title and description can be inferred from the existing menu but:
    The title should be short - remember it will appear in the left-hand job list as well. The names of programs should not appear unless really necessary to distinguish similar tasks.
    The description should amplify on the functionality and include names of key programs run.

    The icon on the menu is taken from a file ccp4i2/qticons/TASKNAME.png.These image files should have resolution at least 32x32 and are currently also displayed at 24x24 in the 'Compact' style - see Preferences.

    Creating Child Pop-out Task GUI

    This is a facility to put a button in the task window that creates another task gui in a pop-out window. The parent CTaskWidget class should also reimplement the CTaskWidget.handleLaunchedJob() method which is called when the child job is created and when it has finished running. There is an example in tasks/dummy/CTaskDummy.py which creates a launch button for the Gesamt task. The result can be seen in the Create New Job folder of the dumMee task (only visble if the Demo folders on the task menu are open). The hypothetical scenario is that the user needs to reposition the input coordinate file by superposing on another model before continuing with this task. The button to lauch and alternative task is defined by the launchButton keyword input to createLine() and it should be followed by the name of the task to be opened. In the example code below the XYZIN input widget and the lauch button are drawn. A connect() call is used to ensure that updateLauchButton() is called whenever the value of XYZIN is changed and this is also called immediately in order to disable the launch button if XYZIN is unset.

        self.createLine(['label','Enter a coordinate file to test launch job button'])
        self.createLine(['widget','XYZIN'])
        self.createLine(['label','Superpose the coordinates on another model by','launchButton','gesamt'])
        # Update the status of the lauch button dependent on selection of XYZIN
        self.connect(self.container.inputData.XYZIN,QtCore.SIGNAL('dataChanged'),self.updateLauchButton)
        # .. and initialise its status
        self.updateLauchButton()
      

    The code to enable/disable the launch button:

      def updateLauchButton(self):
        # Enable the launch button dependent on whether XYZIN is set
        self.findChild(QtGui.QWidget,'gesamt').setEnabled(self.container.inputData.XYZIN.isSet())
    

    Note that the findChild() method is used to get a reference to the launch button. The launch button has been created with its object name set to the name of the task to be launched and this is the second argument to findChild().

    You should also implement an updateLauchButton() method which will be called twice: firstly when the child task window is created and the input status value is 1 (Pending - see top of dbapi/CCP4DbApi.py file) and then when the job has finished so status is 6 (Finished) or 5 (Failed). Both calls also pass the jobId but only the first call passes the a reference to the new task widget; beware the task widget may have been closed by the time the job finishes. The example code shows how to copy data from the current task wigdet into the newly launched task widget and then how to get information on job output files from the database in order to use it in the current gui.

      def handleLaunchedJob(self,jobId=None,status=None,taskWidget=None):
        # If this is called with status=1=Pending the taskWidget (gesamt window) has just been opened
        # and we can set a value in it
        if status == 1 and taskWidget is not None:
          taskWidget.container.inputData.XYZIN_QUERY.set(self.container.inputData.XYZIN)
        # Status is 6 (Finished)
        elif status == 6:
          # Can not assume that the gesamt widget is still there - must instead query the database for output file
          # Use CDbApi.getJobFilesInfo() which returns a list of dicts containing description of files output by the job
          # The best way to set the file object ot a new value is by setDbFileId()
          import CCP4Modules
          gesamtFileList = CCP4Modules.PROJECTSMANAGER().db().getJobFilesInfo(jobId=jobId,jobParamName='XYZOUT')
          if len(gesamtFileList)>0:
            self.getWidget('XYZIN').model.setDbFileId(gesamtFileList[0]['fileId'])
     

    There are some possible problems that are not trapped here - what if the user changes the XYZIN in the current gui but still runs the gesamt task? An exercise for the reader perhaps?

    Speeding drawing of tasks

    Complex task inputs take a significant amount of time to draw which can be reduced by drawing only the top 'Data input' tab when the task is opened and drawing other tabs on demand. To implement this the openFolder() call should have a drawFolder argument that is the method to draw the contents of the folder. So in the aimless task:

    
        def drawContents(self):
      
            self.openFolder(folderFunction='inputData',title='Input Data')
            #define the Input data folder here
    
            self.openFolder(title='Important Options',drawFolder=self.drawImportant)
    
            self.openFolder(title='AdditionaOptions',drawFolder=self.drawAdditional)
    
    
        def drawImportant(self):
            #define the 'Important Options' folder here
          
    
        def drawAdditional(self):
            #define the 'Additional Options' folder here
    
    

    There are likely to be additional issues:
    Code to initialise gui status may need to be put in an appropriate drawFolder method.
    It may be necessary to test whether a given widget is already drawn by calling CTaskWidget.getWidget(PARAMNAME) which wil return None if the widget for PARAMNAME is not yet drawn.
    There are potential problems if the 'toggle' status (visible/invisible) of a line or subfolder is dependent on parameters in another folder other than the top 'Input data' folder. This is not a nice feature anyway.