Hi Tim,
Until the development guide for the ArtistView is available, the only way to see how to create your own tabs or menu items is to look at the current tab or menu item examples.
Before we start, in ArtistView, you have the ability to add/remove right-click menu items and/or lower tabs. You do not have access to any buttons or the primary job/subjob/frame/host tabs.
For ArtistView, there is a "plugins" directory that contains all of the menu items and lower tabs - each is a python script & are named in a way that should make them easy to find (though the name is irrelevant). On Windows and Linux, the plugins dir lives next to the qubeArtistView executable. On OS X, the plugins dir is in the applications package - qubeArtistView.app/Contents/Resources/plugins.
The idea behind the plugins is that you have full access to the currently selected job/subjob/frame(s) & can do with that information anything you'd like. If we look at the QubeMenuJobKill.py example....
There is a:
class UserUIPlugin(QbUIPlugin)
This must be the class name for any custom plugin.
The plugin is initialized with:
def __init__(self):
super(UserUIPlugin,self).__init__()
self.name = "Kill Job" # Display name of the plugin
self.type = "menu" # type of plugin - menu or tab
self.context = "job" # context in which the plugin will show
self.permission = "kill" # Qube permission required to perform this task (optional)
self.hidden = False # Flag to hide this plugin (default is False)
self.sort_order = 5 # order this will show up in the menu
The code should explain what each item does. The "context" can be any one of "job", "subjob", "host", or "frame".
next, there is an askUser function. This function will present an "Are you sure" style dialog to the user prior to the body of the plugin being run. The only parameter of this function kwargs "**selected" - this is a dictionary of selected jobs, subjobs, hosts, and frames, where each is the corresponding qb API class (qb.Job qb.Subjob qb.Host and qb.Work, respectively). I'll post the whole function here as it contains documentation for itself:
def askUser(self, **selected):
"""
This function is designed to provide a method to allow a dialog to be presented
to the user prior to execution. The result will be available to the run function
as a True or False value attached to user_response in the 'selected' kwargs param
of the run function, e.g. selected['user_response']
Return dict should have at least one of the following keys:
text (reqired): Main text of the dialog.
ex: 'You are about to do something...'
title: Text displayed in the title bar of the dialog.
ex: 'Doing Something.'
info_text: Smaller text under the main.
ex: 'Do you really want to do this thing to these things?'
detailed_text: [Long form] Details about what is going to happen - hidden by default.
ex: 'This thing you are about to do is going to do this, and this,
and that; and is irreversible. Potentially effected jobs are:....'
icon: Type of icon to show on the dialog. Options are the strings:
'information', 'question', 'warning', 'critical', 'noicon'
ex: 'information'
button: List/tuple of button(s) to display. Options are the strings:
'ok', 'cancel'; 'save', 'discard'; 'yes', 'no'; 'abort', 'close'.
Note: All positive options make the dialog return True,
all negative options return False.
True: ok, yes, save, apply.
ex: ('yes','no')
"""
jobs = [j['id'] for j in selected.get('jobs',[])]
jobs_str = ', '.join(map(str,jobs))
return {"text":"Killing job(s) %s" % jobs_str,
"info_text":"Do you really want to kill these job(s)",
"detailed_text":"Killing jobs: %s" % jobs_str,
"title":"Kill Jobs?",
"icon":"question",
"buttons": ("Yes","No")}
Lastly, there is the "run" function. This is the function that actually does the work. Like askUser, its only parameter is the kwargs param **selected. Like askUser, it holds a dictionary of all selected jobs, hosts, subjobs, and frames, but this time, it also holds the user's response to the askUser function (either True or False). Like askUser, I'll post the entire bit of code here for its own documentation:
def run(self,**selected):
"""
This is the function that will be run by the UI. There is no return value.
When the run function completes, a signal is emitted to the UI to update
the job(s) that were effected. This signal will be sent regardless of whether
or not the user responded positively to the challenge.
The parameter 'selected' will have most, if not all, of the following keys:
jobs: A list of the selected jobs in the UI
Each list item is a subclass of a qb.Job & therefore has all the attributes
that a qb.Job would have.
subjobs: A list of the selected subjobs
Each list item is a subclass of a qb.Subjob
frames: A list of the selected frames
Each list item is a subclass of qb.Work
hosts: A list of the selected hosts
Each list item is a subclass of qb.Host
user_response: A True/False response from the user, if the user was challenged
(which happens when this plugin defines an 'askUser(**selected)' method.
Note: All positive options make the dialog return True,
all negative options return False.
True: ok, yes, save, apply.
ex: ('yes','no')
"""
jobs = [j.get('id') for j in selected.get('jobs',{})]
# Confirm with user:
if not selected.get('user_response',True):
logging.info("Skipping job kill because of user response")
return
logging.info("Attempting to kill job(s): %s" % ', '.join(map(str,jobs)))
killed = qb.kill(*jobs)
logging.info("Supervisor is killing job(s): %s" % ', '.join(map(str,killed)))
You'll notice that the run function is calling the Qube API to do its work. This is a handy feature, but is in no way necessary. This is to say that in your own plugin, you don't have to do any qb* API calls in the run function - it could process an image, or submit information to a production tracking system, or launch an application, or turn off a computer... Anything that wou can do in python can be done in this plugin space. Note, however, that you are running PFX supplied python 2.6.8. If you need to run custom modules, you will likely have to append to sys.path before importing. Alternatively, you could spawn a second python process that runs your own python.
Note: this is the plugin architecture for ArtistView v1.0. It may change moving forward.
Please feel free to ask any questions or give any comments that come up. This is a new feature for Qube - we'd love to get more feedback on its utility.
-Brian