Versions Compared

    Key

    • This line was added.
    • This line was removed.
    • Formatting was changed.
    Comment: Published by Scroll Versions from this space and version 7.0-0

    ...

    • You can easily navigate to the stock plugins dir by going to File > Open Plugins Directory
    • You can add additional plugins path(s) under the Plugins tab in the preferences
    • You can add additional plugins path(s) by setting a comma-separated list of paths in an environment variable called ARTISTVIEW_ADDITIONAL_PLUGINS_PATHS
      Image RemovedImage Added 

    Adding Custom Right Tabs

    All of the right-side tabs in ArtistView are driven by Python plugins.  Depending on the type of tab displayed, the plugin's job is to retrieve data from the selected items (jobs, instances, frames, or hosts) and present some type of data to be displayed.  While ArtistView is written in PyQt, no knowledge of PyQt is needed to create a plugin.

    Plugin Layout

    • All plugins are of class UserUIPlugin and extend QbUIPlugin.
    • In the class's __init__ function, the following are defined:
      • Required:
        • name: the display name of the plugin
        • type: the type of plugin.  Options:
          • tab: this plugin creates a new tab
          • menu: this plugin creates a right-click menu item
        • context: Where the tab will be displayed. Options:
          • job: this tab plugin will be shown when the job list is visible.
          • host: this plugin will be shown with the host/workers list is visible.
        • tab_type: the type of tab plugin.  See above "Types of Tab Plugins" for a list of options.
        • functions: a dictionary that maps UI focus to a plugin function.  As an example, a tab plugin showing logs should show the job log when the job list has focus, an instance log when the instance list has focus, and a frame log when the frame list has focus. More information on the details of the user-defined function can be found in the section below entitled "Accessing Qube Information from the Plugin"
      • Optional:
        • hidden: Setting this to true makes the function completely hidden from the UI - it will not even show up in the preferences.
        • search_field: Setting this to true displays a search field below the content.  That search field will search through displayed content.  This only has meaning for "html" plugins.
        • frame_slider: Setting this to true displays a frame slider below the content. This only has meaning for "preview" plugins.
        • aspect_mode: For the preview tab, this drives how the frame will fit into the window. Options:
          • -1: display the original image - do not resize or scale.
          • 0: ignore aspect ratio - stretch the image to fully fill the display window.
          • 1: resize to fit within the display window, but maintain aspect ratio.
          • 2: resize to fit shortest dimension into the display window, and maintain aspect ratio
        • sort_order: order in which to display this tab in the list of tabs
    • Beyond the __init__, only the functions defined in the functions member variable are required.  They should return the data specified in the "Types of Plugins" section above.

    Anchor
    pluginTabType
    pluginTabType
    Types of Tab Plugins (tab_type attribute)

    • html
      • Example: The  The stock "Job Properties" tab
      • Will display basic html but not much in the way of css or javascript.  
      • The functional goal of this plugin is to return basic html as a string.
    • webkit
      • Example: The stock "Thumbnails" tab.
      • Will display advanced html, including css and javascript - much like a typical web browser. 
      • The functional goal of this plugin is to return a complete [x]html page.
    • tree
      • Example: QubeTabTreeExample.py in the plugins directory
      • Will display a list of [nested] lists in a tree widget.
      • The functional goal of this plugin is to return a [nested list of] list[s].
    • preview
      • Example: The stock "Preview" tab.
      • Will create an image viewer with scrubber for flipping between flipping between output frames. 
      • The functional goal of this plugin is to return a list of paths to images.
    • openGL:
      • Deprecated in 6.5-0

    ...

    In order for the plugin to do anything meaningful, it must know what entities are selected and have access to the Qube data that drives those entities. Any user function defines must take the kwarg "**selected".  Selected will  will be a dictionary containing lists of items that are selected in the interface.  The dictionary is keyed on the type of item.  Regardless of the function, selected will always contain the following keys:

    ...

    Working with Permissions

    Qube has a set of internal internal permissions viewable  viewable under the the WV User Permissions in WranglerView.  Those permissions determine which users can perform which actions and are ultimately controlled by the supervisor.  ArtistView can reflect those permissions in its interface by assigning a permission member  member variable to the permission required to perform the actions performed by the plugin.  For example, in order to block a job, one must have the "block" permission.  In order the modify a job, one must have the "modify" permission.

    ...

    If a user does not have required permission to run the plugin, then the plugin will not show at all.  If the user has required permission, but is not an admin, then the plugin will be visible but disabled when the user attempts to perform the action on any entity they do not own (for example, one cannot modify someone else's job unless they are an admin).

    Accessing Qube Information from the Plugin

    In order for the plugin to do anything meaningful, it must know what entities are selected and have access to the Qube data that drives those entities. Any user function defines must take the kwarg "**selected".  Selected will be a dictionary containing lists of items that are selected in the interface.  The dictionary is keyed on the type of item.  Regardless of the function, selected will always contain the following keys:

    ...

    If you would like to set a search field and perform a search, programmatically, from a plugin, you do so by adding an updateUI method to the plugin class. This function should return a dictionary of UI fields that need updating.  For 6.7, the only UI fields that can be updated are the search fields which are denoted by the keys keys search_jobs, search_frames, search_instances, or or search_workers for  for the search field in the job list, frame, list, instance list, or worker list, respectively.

    ...

    ..which would return a dictionary that looks like {"search_jobs":"pgrp:1234 OR pgrp:1235"}.  This would then fill the job list search field with with pgrp:1234 OR pgrp:1235 and perform the search.

    ...

    Code Block
    titleQubeMenuJobHelloWorld.py
    linenumberstrue
    languagepython
    from view.qubeArtistViewUIPlugin import QbUIPlugin
    import qb
    import logging
    class UserUIPlugin(QbUIPlugin):
        """
        UserUIPlugin - user created plugins for Qube UI to add functinality
        to the interface.
        A plugin must supply a type, name, context.  A menu plugin must supply
        a 'run' function.
         'name' is the display name of the plugin
         'context' determines in which menus/areas this [menu] plugin
         will be displayed. Current available contexts are job, subjob,
         frame, host.
         'type' is the type of plugin - menu or tab
         'permission' is the *Qube* permission required to run this plugin
         'hidden' is a flag that, if set to True, will prevent the menu
         item from displaying.
         'sort_order' determines the order in which this item will show
         in the menu. It relies on the fact that other [menu] plugins also define a
         number.  If the number isn't defined, it's assigned the default of 999.
        The plugin's run function passes, as kwargs, the
         currently selected job(s), subjob(s), frame(s), host(s), and/or user
         response if they were challenged by an askUser function definition.
         Each job, subjob, frame, or host, is a qb.Job, qb.Subjob, qb.Work,
         or qb.Host, respectively, and can be operated on as one would with
         the qb API.
        """
        def __init__(self):
            super(UserUIPlugin,self).__init__()        
            self.name    = "Hello World (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 = None              # Qube permission required to perform this task (optional)
            self.hidden  = True                 # Flag to hide this plugin (default is False)
            self.sort_order = 15                # order this will show up in the menu
    
        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')
            """
            job_ids = [j.get('id') for j in selected.get('jobs',[])]
            jobs_str = ', '.join(map(str,job_ids))
            return {"text":"Hello world 'text' for job(s) %s" % jobs_str,
                    "info_text":"Hello world 'info_text' for job(s)",
                    "detailed_text":"Hello world 'detailed_text' for jobs: %s" % jobs_str,
                    "title":"Hello world 'title'",
                    "icon":"information",
                    "buttons": ("Ok","Cancel")}
    
        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')
            """
            job_ids = [j.get('id') for j in selected.get('jobs',[])]
            # Confirm with user:
            if not selected.get('user_response',True):
                logging.info("Skipping job hello world because of user response")
                return
            logging.info("Hello world plugin has completed complete for jobs %s" % ', '.join(map(str,job_ids)))