Differences

This shows you the differences between two versions of the page.

Link to this comparison view

muster:9.0:builtin [2018/01/10 14:01] (current)
Line 1: Line 1:
 +====== Muster Builtin scripting ======
 +
 +Starting with version 8.5.0, Muster supports integrated scripting through the Console interface or extending the python functions inside any template. The Python scripting API let you customize behaviors of the Console, the Dispatcher and any Renderclient instance.
 +
 +The Python modules available are:
 +
 +  * Dispatcher : MDispatcherAPI , gives access to the entire Dispatcher data structures
 +  * Renderclient : MInstanceAPI , gives access to the Renderclient status as well as the running jobs
 +  * Console : MConsoleAPI , gives access to the Console data structures. It also gives access to the running QT Gui libraries to create custom UI inside Console
 +
 +==== Usage scenario ====
 +
 +Here are some idea in what contexts you can use the builtin Muster scripting facilities:
 +
 +==== Dispatcher scripting ====
 +
 +One of the most useful script you can write on the Dispatcher is to perform background tasks.
 +You can script the Dispatcher from within the global template to perform idling tasks using the template member function onDispatcherIdling() available only in the global template (0). This function is invoked automatically by the Dispatcher every 60 seconds.
 +If you need to call it more or less frequently, you can change the idling time by setting the preferences like in the example below:
 +
 +<code python>
 +def onDispatcherIdling(self):​
 +    import MClientAPI
 +    import MDispatcherAPI
 +    config = MClientAPI.MDispatcherConfiguration()
 +    err = MDispatcherAPI.systemGetConfiguration(config)
 +    config.tuning.setIdlingScriptingTime(10) # Execute this function each 10 seconds
 +    MDispatcherAPI.systemSetConfiguration(config,​0) # We do not store the settings in the dispatcher.conf file, just flush back the changes in memory
 +    ​
 +    # Now executes your idling function, in this example , we automatically lock completed jobs
 +    err, jobs = MDispatcherAPI.jobGetList()
 +    for job in jobs:
 +        if (job.getStatus() == MClientAPI.MJob.kJobsStatusJobCompleted):​
 +            job.setLocked(1) # Locks the job
 +            MDispatcherAPI.jobUpdate(job.getJobId()) # Refresh the job status to connected Consoles
 +</​code>​
 +
 +Other useful tasks may be performed on the Dispatcher using the onJobCompleted() and onJobStart() templates functions. Also you can register a script to be run on the Dispatcher by registering a new script into the Console and select the Dispatcher as the execution context. This will make the script on-demand from the Console GUI.
 +
 +==== Client scripting ====
 +
 +One of the most useful script you can write on the client is to perform background tasks as well as checking your client availability.
 +
 +You can script the client from within the global template to perform idling tasks using the template member function onClientIdling() available only in the global template (0). This function is invoked automatically by the client every 60 seconds.
 +If you need to call it more or less frequently, you can change the idling time by setting the preferences like in the example below:
 +<code python>
 +def onClientIdling(self):​
 +    import MClientAPI
 +    import MInstanceAPI
 +    config = MClientAPI.MNodeConfiguration()
 +    err = MInstanceAPI.getConfiguration(config)
 +    config.tuning.setIdlingScriptingTime(10) # Execute this function each 10 seconds
 +    MInstanceAPI.setConfiguration(config,​0) # We do not store the settings in the dispatcher.conf file, just flush back the changes in memory
 +    ​
 +    # Now executes your idling function
 +</​code>​
 +
 +<code python>
 +def onCheckClientAvailability:​
 +    import MClientAPI
 +    import MInstanceAPI
 +    err, status = MInstanceAPI.getInstanceStatus(0) # Grabs the status of instance 0, you should tweak this if running multiple instances by collecting the overall status
 +    if (status == MClientAPI.MNode.kInstanceStatusInprogress):​
 +        # There'​s a rendering in progress, check what job is rendering
 +        err, job = MInstanceAPI.getRunningJob()
 +        if (job.getName() == "My very special job"):
 +            # Do not stop the client availability but confirm it
 +            return MTemplateAPI.kTemplateFunctionSuccess # Client will be available for rendering
 +            ​
 +    # Otherwise calculates the default availability
 +    return MTemplateAPI.kTemplateFunctionDoDefault
 +</​code>​
 +
 +==== Console scripting ====
 +
 +Here there are unlimited capabilities. With the inclusion of PySide inside the Muster python distribution,​ you can write your own GUI to react to context menus inside the Muster views as well as create your own submission or job checking dialogs.
 +
 +One of the most useful implementation may be to create a custom dialog to validate any job you submit into Console. This can be easily done writing the onJobValidate() on the global template (0) or any specific engine template. This is a full example that asks for an extra job attribute at the time of the submission, you can then recall your custom attribute on any template or script:
 +
 +<code python>
 +# The following script goes at the top of the global template, it declares a Job Validator Class only if running inside the Console context
 +
 +if (muster.runningContext() == "​console"​):​
 +    ​
 +    from PySide import QtCore, QtGui
 +    import MConsoleAPI
 +
 +    class JobValidatorDialog(QtGui.QDialog):​
 +    ​
 +        def __init__(self):​
 +            super(JobValidatorDialog,​self).__init__()
 +            self.initUI()
 +        ​
 +        def initUI(self):​
 +            self.layout = QtGui.QVBoxLayout()
 +            ​
 +            self.label = QtGui.QLabel("​Job shot:"​)
 +            self.shot = QtGui.QLineEdit()
 +            self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok| QtGui.QDialogButtonBox.Cancel)
 +            ​
 +            self.layout.addWidget(self.label)
 +            self.layout.addWidget(self.shot)
 +            self.layout.addStretch()
 +            self.layout.addWidget(self.buttonBox)
 +            ​
 +            self.setLayout(self.layout)
 +            self.buttonBox.accepted.connect(self.accept)
 +            self.buttonBox.rejected.connect(self.reject)
 +</​code>​
 +
 +Then, at the template definition level, you can change the onValidateJob function in this way:
 +
 +<code python>
 +    def onValidateJobSubmission(self,​ job, err):
 +        if (muster.runningContext() != "​console"​):​ return 1 # Skip validation on no console contexts
 +        dlg = JobValidatorDialog()
 +        result = dlg.exec()
 +        if (result == QtGui.QDialog.Accepted):​
 +            job.attributeSetString("​JOBSHOT", ​ dlg.shot.text(),​ 1 ,0) # If user clicks OK, a new attribute is appended to the job, it can be later checked from the templates, or shown in console by customizing the job view headers
 +            return 1
 +        else:
 +            err.setString("​Cancelled by user!"​)
 +            return 0
 +</​code>​