Skip to content
manne ohrstrom edited this page May 20, 2014 · 58 revisions

The Shotgun Utils Framework contains a collection of helpers and tools to make it easy and painless to build consistent looking applications. It contains several modules which can each be used independently:

  • A Shotgun Data Model deriving from QStandardItemModel which makes it easy to build responsive, data rich applications quickly.
  • A delegates framework which makes it easy to build custom views inside QT's view classes. Typically used in conjunction with the Shotgun Model.
  • A help popup module which makes it easy to deliver interactive documentation queues to users.
  • A settings system handles per-user preferences and makes it easy to work with the QSettings class from within your app code.
  • A progress overlay module provides a standardized progress overlay widget which can easily be placed on top of any other QWidget to indicate that work is happening and potentially report messages back to the user.
  • An asynchronous shotgun data receiver which makes it easy to work with background queries and thumbnail retrieval

In the following documentation sections, each module is presented in detail with API reference and code examples.

Shotgun Model

The shotgun data model helps you build responsive, data rich applications quickly and leverage QT's build in model/view framework

The Shotgun Model is a custom QT Model specialized for Shotgun Queries. It uses a disk based cache and runs queries asynchronously to Shotgun in the background for performance. In a nutshell, you derive your own model class from it, set up a query, and then hook up your model to a QT View which will draw the data. The class contains several callbacks and allows for extensive customization, yet tries to shadow and encapsulate a lot of the details.

For convenience, three different classes are provided, allowing you to choose the right level of encapsulation.

Why should I use the Shotgun Model?

Using the Shotgun Model means switching to Model/View based programming. While there is perhaps slightly more overhead to get started with this, there are many benefits. The Shotgun Model (and the corresponding delegates and Shotgun View components) is an attempt to bridge this gap and make it quick and painless to get started with QT Model/View programming.

QT provides a strong and mature Model/View hierarchy which is robust and easy to work with. If you are not familiar with it, please check the following links:

The benefits with this approach will become evident as you scale your UIs and their complexity. Developing code and tools where the data and the UI is combined will work in simple scenarios but for data rich applications this approach becomes hard to maintain, difficult to reuse and typically scales poorly as the dataset complexity grows. By leveraging QTs built-in functionality, you get access to a mature and well documented toolbox that makes it quick to develop tools:

  • A Shotgun model instance represents a single shotgun query. With two lines of code you can connect the resultset of such a query with a standard Qt list, tree or table.
  • The Shotgun model is cached, meaning that all data is fetched in the background in a worker thread. This means that the data in your UI will load up instantly and you never have to wait for shotgun. If the query result is different than the cached result, the view will be updated on the fly as the data arrives.
  • With QT you have access to SelectionModels, making it easy to create consistent selection behaviour, even across multiple views. With full keyboard support.
  • With QT proxy models you can easily create interactive searching and filtering on the client side.
  • Views and models are optimized and will perform nicely even if you have thousands of items loaded.
  • Through the shotgun view module, you can easily control the QT delegates system, making it easy to draw custom UIs for each cell in your view.

Shotgun Model Hello World

A hello world style example would look something like this, assuming this code is inside a toolkit app:

# Import the shotgun_model module from the shotgun utils framework
shotgun_model = tank.platform.import_framework("tk-framework-shotgunutils", "shotgun_model") 
# Set up alias
ShotgunModel = shotgun_model.ShotgunModel 

# Create a standard QT Tree View
view = QtGui.QTreeView(parent_widget)

# Set up our data backend
model = shotgun_model.SimpleShotgunModel(parent_widget)

# Tell the view to pull data from the model
view.setModel(model)

# load all assets from Shotgun
model.load_data(entity_type="Asset")

The above code will create a standard QT treeview of all assets in Shotgun.

Beyond Hello World

The simple setup outlined above could be extended in the following ways:

  • If you need more control of how the data is being retrieved, consider instead creating your own class and derive from ShotgunOverlayModel. This makes it possible to customize the shotgun data as it arrives from Shotgun, control the hierarchy grouping and many other things.
  • If you want to retrieve results from your view, connect signals to the view's selection model.
  • If you want to cull out items from the model, for example only to show items matching a particular search criteria, use a Proxy Model (typically QSortFilterProxyModel).
  • If you want to control the way items are displayed in the view, consider using the Shotgun delegates module documented below.

Data Items

The Shotgun Model derives from QStandardItemModel which is a base model which managed the storage of model data inside a collection of QStandardItem objects. Each of these objects have a number of standard property and so called roles, holding various pieces of data such as icons, colours etc. The Shotgun Model introduces two new standard roles which can be used by both subclassing and calling code:

  • ShotgunModel.SG_DATA_ROLE holds the shotgun data associated with an object. In a tree view, only leaf nodes have this data defined - other nodes have it set to None. For leaf nodes, it is a standard shotgun dictionary containing all the items that were returned by the Shotgun query.
  • ShotgunModel.SG_ASSOCIATED_FIELD_ROLE holds the associated field value for a node. This is contained in a dictionary with the keys name and value. For example, for a leaf node this is typically something like {"name": "code", "value": "AAA_123"}. For an intermediate node, it may be something such as {"name": "sg_sequence", "value": {"id": 123, "name": "AAA", "type": "Sequence"} }.

Datetime objects and the Shotgun API

Because of issues with serialization, please note that datetime objects returned by the Shotgun API are automatically converted to unix timestamps by the model. If you want to convert a unix time stamp to a string, you can use the following code:

import datetime
date_str = datetime.datetime.fromtimestamp(unix_time).strftime('%Y-%m-%d %H:%M')

Class SimpleShotgunModel API Reference

Convenience wrapper around the Shotgun model for quick and easy access. Use this when you want to prototype data modeling or if your are looking for a simple flat data set reflecting a shotgun query. All you need to do is to instantiate the class (typically once, in your constructor) and then call load_data to specify which shotgun query to load up in the model. Subsequently call load_data whenever you wish to change the Shotgun query associated with the model.

This class derives from ShotgunModel so all the customization methods available in the normal ShotgunModel can also be subclassed from this class.

SimpleShotgunModel Constructor

Class constructor. The simple shotgun model contains a progress spinner which will appear whenever the object is doesn't have any data to display. This progress spinner will be placed on top of the parent object specified in the parent constructor parameter.

SimpleShotgunModel(parent)

Constructor Parameters

  • QWidget parent - QWidget which this model will be parented under. This widget will also be used to paint a spinner and display error messages.

load_data()

Loads shotgun data into the model, using the cache if possible. The model is not nested and the first field that is specified via the fields parameter (code by default) will be used as the default name for all model items.

model_obj.load_data(entity_type, filters=None, fields=None)

Parameters & Return Value

  • str entity_type - Shotgun Entity Type to load data for.
  • list filters - Shotgun API find-style filter list. If no list is specified, all records for the given entity type will be retrieved.
  • list fields - List of Shotgun fields to retrieve. If not specified, the 'code' field will be retrieved.

Class ShotgunOverlayModel API Reference

Convenience wrapper around the ShotgunModel class which adds spinner and error reporting overlay functionality. Where the ShotgunModel is a classic model class which purely deals with data, this class connects with a QWidget in order to provide progress feedback whenever necessary. Internally, it holds an instance of the ShotgunOverlayWidget widget described further down in this documentation and will show this whenever there is no data to display in the view. This means that it is straight forward to create shotgun views with a spinner on top indicating when data is being loaded and where any errors are automatically reported to the user.

Signal progress_spinner_start

progress_spinner_start()

Signal that gets emitted whenever the model deems it appropriate to indicate that data is being loaded. Note that this signal is not emitted every time data is loaded from Shotgun, but only when there is no cached data available to display. This signal can be useful if an implementation wants to set up a custom overlay system instead of or in addition to the built in one that is provided via the set_overlay_parent() method.

ShotgunOverlayModel Constructor

Constructor. This will create a model which can later be used to load and manage Shotgun data.

ShotgunOverlayModel(parent, overlay_widget, download_thumbs=True, schema_generation=0)

Constructor Parameters

  • QObject parent - Parent object.
  • QWidget overlay_widget - Widget on which the spinner/info overlay should be positioned.
  • bool download_thumbs - Boolean to indicate if this model should attempt to download and process thumbnails for the downloaded data.
  • int schema_generation - Schema generation index. If you are changing the format of the data you are retrieving from Shotgun, and therefore want to invalidate any cache files that may already exist in the system, you can increment this integer.

_hide_overlay_info()

Hides any overlay that is currently shown, except for error messages.

model_obj._hide_overlay_info()

_show_overlay_pixmap()

Show an overlay status message in the form of a pixmap. This is for example useful if a particular query doesn't return any results. If an error message is already being shown, the pixmap will not replace the error message.

model_obj._hide_overlay_info(pixmap)

Parameters & Return Value

  • QPixmap pixmap - QPixmap object containing graphic to show

_show_overlay_info_message()

Show an overlay status message. If an error is already displayed, this info message will not be shown.

model_obj._show_overlay_info_message(msg)

Parameters & Return Value

  • str msg - message to display
  • Returns: True if the message was shown, False if not.

_show_overlay_error_message()

Show an overlay error message.

model_obj._show_overlay_error_message(msg)

Parameters & Return Value

  • str msg - message to display

Class ShotgunModel API Reference

A QT Model representing a Shotgun query.

This class implements a standard QAbstractItemModel specialized to hold the contents of a particular Shotgun query. It is cached and refreshes its data asynchronously.

In order to use this class, you normally subclass it and implement certain key data methods for setting up queries, customizing etc. Then you connect your class to a QView of some sort which will display the result. If you need to do manipulations such as sorting or filtering on the data, connect a QProxyModel between your class and the view.

The model can either be a flat list or a tree. This is controlled by a grouping parameter which works just like the Shotgun grouping. For example, if you pull in assets grouped by asset type, you get a tree of data with intermediate data types for the asset types. The leaf nodes in this case would be assets.

Signal query_changed

query_changed()

Signal which gets emitted whenever the model's sg query is changed. when the query changes, the contents of the model is cleared and the loading of new data is initiated.

Signal cache_loaded

cache_loaded()

Signal which gets emitted whenever the model loads cache data. this typically follows shortly after a query changed signal, if cache data is available.

Signal data_refreshing

data_refreshing()

Signal which gets emitted whenever the model starts to refresh its shotgun data. This is emitted from _refresh_data() at the beginning of a refresh operaton. Useful signal if you want to present a loading indicator of some kind.

Signal data_refreshed

data_refreshed(bool)

Signal which gets emitted whenever the model has been updated with fresh shotgun data. The boolean indicates that a change in the model data has taken place as part of this process. If the refresh fails for some reason, this signal may not be emitted.

The synchronous data refresh cycle starts with a call to _refresh_data() and normally ends with either a data_refreshed or a data_refresh_fail being emitted. The exception being that if you call _load_data() or clear the model in some other way, the signals may never be emitted.

Signal data_refresh_fail

data_refresh_fail(str)

Signal which gets emitted in the case the refresh fails for some reason, typically due to the absence of an Internet connection. This signal could for example be used to drive a "retry" button of some kind. The str parameter carries an error message with details about why the refresh wasn't successful.

ShotgunModel Constructor

Constructor. This will create a model which can later be used to load and manage Shotgun data.

ShotgunOverlayModel(parent, download_thumbs=True, schema_generation=0)

Constructor Parameters

  • QObject parent - Parent object.
  • bool download_thumbs - Boolean to indicate if this model should attempt to download and process thumbnails for the downloaded data.
  • int schema_generation - Schema generation index. If you are changing the format of the data you are retrieving from Shotgun, and therefore want to invalidate any cache files that may already exist in the system, you can increment this integer.

set_shotgun_connection()

Specify the shotgun api instance this model should use to communicate with Shotgun. If not specified, each model instance will instantiate its own connection, via toolkit. The behavior where each model has its own connection is generally recommended for thread safety reasons since the Shotgun API isn't natively thread-safe.

sg_model_obj.set_shotgun_connection( sg )

Parameters & Return Value

  • Shotgun sg - Shotgun API instance.

destroy()

Call this method prior to destroying this object. This will ensure all worker threads etc are stopped. The method does not return a value but it is blocking and may be slow to execute.

sg_model_obj.destroy()

hard_refresh()

Clears any caches on disk, then refreshes the data.

sg_model_obj.hard_refresh()

item_from_entity()

Tried to locate an item contained in the model matching the given entity type and entity id. Returns a QStandardItem if found, returns none if not found.

sg_model_obj.item_from_entity( entity_type, entity_id )

Parameters & Return Value

  • string entity_type - Shotgun entity type to look for.
  • int entity_id - Shotgun entity id to look for.
  • Returns: QStandardItem or None if no object could be found.

get_filters()

Returns a list of Shotgun filters representing the given item. This is useful if you are trying to determine how intermediate leaf nodes partition leaf node data.

sg_model_obj.get_filters( item )

For example, if you have created a hierarchical model for a Shot listing: [sg_sequence, sg_status, code], the Shotgun model will group the data by sequence, then by status. Finally, the leaf nodes will be the shot names. If you execute the get_filters() method on a sequence level tree node, it may return

[ ['sg_sequence', 'is', {'type': 'Sequence', 'id': 123, 'name': 'foo'}] ]

If you execute the get_filters() on a status node in the tree, it may return

[ ['sg_sequence', 'is', {'type': 'Sequence', 'id': 123, 'name': 'foo'}],
    ['sg_status', 'is', 'ip'] ]

Parameters & Return Value

  • QStandardItem item - One of the QStandardItem model items that is associated with this model.
  • Returns: standard shotgun filter list to represent that item.

get_entity_type()

Returns the Shotgun Entity type associated with this model.

sg_model_obj.get_entity_type()
  • Returns: Shotgun entity type string (e.g. 'Shot', 'Asset' etc).

Protected methods not meant to be subclassed

These methods are typically called from within the subclass, but they are not meant to be subclassed themselves. If you do decide to subclass them, carefully ensure that you call the base class implementation for correct behaviour.

_load_data()

This is the main method to use to configure the model. You basically pass a specific find query to the model and it will start tracking this particular set of filter and hierarchy parameters. Any existing data in contained in the model will be cleared. This method will not call the Shotgun API. If cached data is available, this will be immediately loaded (this operation is very fast even for substantial amounts of data). If you want to refresh the data contained in the model (which you typically want to!), call the _refresh_data() method.

sg_model_obj._load_data( entity_type, filters, hierarchy, fields, fields, order=None, seed=None )

Parameters

  • str entity_type - Shotgun entity type to download
  • str filters - List of Shotgun filters. Standard Shotgun syntax.
  • str hierarchy - List of grouping fields. These should be names of Shotgun fields. If you for example want to create a list of items, the value ["code"] will be suitable. This will generate a data model which is flat and where each item's default name is the Shotgun name field. If you want to generate a tree where assets are broken down by asset type, you could instead specify ["sg_asset_type", "code"].
  • str fields - Fields to retrieve from Shotgun (in addition to the ones specified in the hierarchy parameter). Standard Shotgun API syntax. If you specify None for this parameter, Shotgun will not be called when the _refresh_data() method is being executed.
  • str order - Order clause for the Shotgun data. Standard Shotgun API syntax. Note that this is an advanced parameter which is meant to be used in subclassing only. The model itself will be ordered by its default display name, and if any other type of ordering is desirable, use for example a QProxyModel to handle this. However, knowing in which order results will arrive from Shotgun can be beneficial if you are doing grouping, deferred loading and aggregation of data as part of your subclassed implementation, typically via the _before_data_processing() method.
  • str seed - Advanced parameter. With each shotgun query being cached on disk, the model generates a cache seed which it is using to store data on disk. Since the cache data on disk is a reflection of a particular shotgun query, this seed is typically generated from the various query and field parameters passed to this method. However, in some cases when you are doing advanced subclassing, for example when you are culling out data based on some external state, the model state does not solely depend on the shotgun query parameters. It may also depend on some external factors. In this case, the cache seed should also be influenced by those parameters and you can pass an external string via this parameter which will be added to the seed.
  • Returns: True if a cached load was successful and the model was populated, False if not.

_refresh_data()

Rebuilds the data in the model to ensure it is up to date. This call is asynchronous and will return instantly. The update will be applied whenever the data from Shotgun is returned. If the model is empty (no cached data), a spinner is shown. If cached data is available, the update happens silently in the background. If data has been added, this will be injected into the existing structure. In this case, the rest of the model is intact, meaning that also selections and other view related states are unaffected. If data has been modified or deleted, a full rebuild is issued, meaning that all existing items from the model are removed. This does affect view related states such as selection.

sg_model_obj._refresh_data()

_request_thumbnail_download()

Request that a thumbnail is downloaded for an item. If a thumbnail is successfully retrieved, either from disk (cached) or via shotgun, the method _populate_thumbnail() will be called. If you want to control exactly how your shotgun thumbnail is to appear in the UI, you can subclass this method. For example, you can subclass this method and perform image composition prior to the image being added to the item object.

Note: This is an advanced method which you can use if you want to load thumbnail data other than the standard 'image' field. If that's what you need, simply make sure that you set the download_thumbs parameter to true when you create the model and standard thumbnails will be automatically downloaded. This method is either used for linked thumb fields or if you want to download thumbnails for external model data that doesn't come from Shotgun.

sg_model_obj._request_thumbnail_download( item, field, url, entity_type, entity_id )

Parameters & Return Value

  • QStandardItem item - StandardItem to request download for.
  • str field - Shotgun field where the thumbnail is stored. This is typically 'image' but can also for example be 'sg_sequence.Sequence.image'.
  • str url - Thumbnail url as returned by Shotgun
  • str entity_type - Shotgun entity type
  • int entity_id - Shotgun entity id

Protected methods to be implemented by subclasses

These methods are not required to be implemented by deriving classes, but in case you need to do advanced customization, the below methods provide a lot of flexibility.

_populate_item()

Whenever an item is constructed, this method is called. It allows subclasses to intercept the construction of a QStandardItem and add additional metadata or make other changes that may be useful. Nothing needs to be returned. This method is called before the item is added into the model tree. At the point when the item is added into the tree, various signals will fire, informing views and proxy models that a new item has been added. This methods allows a subclassing object to add custom data prior to this.

Note that when an item is fetched from the cache, this method is not called, it will only be called when shotgun data initially arrives from a Shotgun API query.

This is typically subclassed if you retrieve additional fields alongside the standard "name" field and you want to put those into various custom data roles. These custom fields on the item can later on be picked up by custom (delegate) rendering code in the view.

sg_model_obj._populate_item( item, sg_data )

Parameters & Return Value

  • QStandardItem item - QStandardItem that is about to be added to the model. This has been primed with the standard settings that the ShotgunModel handles.
  • dict sg_data - Shotgun data dictionary that was received from Shotgun given the fields and other settings specified in _load_data().

_populate_default_thumbnail()

Called whenever an item is constructed and needs to be associated with a default thumbnail. In the current implementation, thumbnails are not cached in the same way as the rest of the model data, meaning that this method is executed each time an item is constructed, regardless of if it came from an asynchronous shotgun query or a cache fetch.

The purpose of this method is that you can subclass it if you want to ensure that items have an associated thumbnail directly when they are first created.

Later on in the data load cycle, if the model was instantiated with the download_thumbs parameter set to True, the standard Shotgun image field thumbnail will be automatically downloaded for all items (or picked up from local cache if possible). When these real thumbnails arrive, the _populate_thumbnail() method will be called.

sg_model_obj._populate_default_thumbnail( item )

Parameters & Return Value

  • QStandardItem item - QStandardItem that is about to be added to the model. This has been primed with the standard settings that the ShotgunModel handles.

_finalize_item()

Called whenever an item is fully constructed, either because a shotgun query returned it or because it was loaded as part of a cache load from disk.

sg_model_obj._finalize_item( item )

Parameters & Return Value

  • QStandardItem item - QStandardItem that is about to be added to the model. This has been primed with the standard settings that the ShotgunModel handles.

_populate_thumbnail()

Called whenever the real thumbnail for an item exists on disk. The following execution sequence typically happens:

  • QStandardItem is created, either through a cache load from disk or from a payload coming from the Shogun API.
  • After the item has been set up with its associated Shotgun data, _populate_default_thumbnail() is called, allowing client code to set up a default thumbnail that will be shown while potential real thumbnail data is being loaded.
  • The model will now start looking for the real thumbail.
    • If the thumbnail is already cached on disk, _populate_thumbnai() is called very soon.
    • If there isn't a thumbnail associated, _populate_thumbnail() will not be called.
    • If there isn't a thumbnail cached, the model will asynchronously download the thumbnail from Shotgun and then (after some time) call _populate_thumbnail().

This method will be called for standard thumbnails if the model has been instantiated with the download_thumbs flag set to be true. It will be called for items which are associated with shotgun entities (in a tree data layout, this is typically leaf nodes). It will also be called once the data requested via _request_thumbnail_download() arrives.

This method makes it possible to control how the thumbnail is applied and associated with the item. The default implementation will simply set the thumbnail to be icon of the item, but this can be altered by subclassing this method.

sg_model_obj._populate_thumbnail( item, field, path )

Parameters & Return Value

  • QStandardItem item - QStandardItem which is associated with the given thumbnail
  • string field - The Shotgun field which the thumbnail is associated with.
  • string path - A path on disk to the thumbnail. This is a file in jpeg format.

_before_data_processing()

Called just after data has been retrieved from Shotgun but before any processing takes place. This makes it possible for deriving classes to perform summaries, calculations and other manipulations of the data before it is passed on to the model class.

sg_model_obj._before_data_processing( sg_data_list )

Parameters & Return Value

  • list sg_data_list - list of shotgun dictionaries, as retunrned by the find() call.
  • Returns: Should return a list of shotgun dictionaries, on the same form as the input.

_load_external_data()

Called whenever the model needs to be rebuilt from scratch. This is called prior to any shotgun data is added to the model. This makes it possible for deriving classes to add custom data to the model in a very flexible fashion. Such data will not be cached by the ShotgunModel framework.

sg_model_obj._load_external_data()

Parameters & Return Value

  • Returns: A list of QStandardItem objects.

Widget Delegate and Standard Widgets

The widget delegates module makes it easy to create custom UI experiences that are using data from the Shotgun Model. It also contains a set of standard widgets.

If you feel that the visuals that you get back from a standard QTreeView or QListView are not sufficient for your needs, these view utilities provide a collection of tools to help you quickly build consistent and nice looking user QT Views. These are typically used in conjunction with the ShotgunModel but this is not a requirement.

  • The WidgetDelegate helper class makes it easy to connect a QWidget of your choosing with a QT View. The WidgetDelegate will use your specified widget when the view is drawn and updated. This allows for full control of the visual appearance of any view.
  • For consistency reasons we also supply a couple of simple widget classes that are meant to be used in conjunction with the WidgetDelegate. By using these widgets in your code you get the same look and feel as all other apps that use the widgets.

QT allows for customization of cell contents inside of its standard view classes through the use of so called delegate classes (see http://qt-project.org/doc/qt-4.8/model-view-programming.html#delegate-classes), however this can be cumbersome and difficult to get right. In an attempt to simplify the process of view customization, the Shotgun Utils framework contains classes for making it easy to plugin in standard QWidgets and use these as "brushes" when QT is drawing its views. You can then quickly produce UI in for example the QT designer, hook up the data exchange between your model class and the widget in a delegate and quickly have some nice custom looking user interfaces!

The principle is that you derive a custom delegate class from the WidgetDelegate that is contained in the framework. This class contains for simple methods that you need to implement. As part of this you can also control the flow of data from the model into the widget each time it is being drawn, allowing for complex data to be easily passed from Shotgun via the model into your widget. For consistency, the framework also contains a number of standard widgets. Using these will provide you with a consistent looking set of visual components.

The following example shows some of the basic around using this delegate class with the Shotgun Model.

# import the shotgun_model and view modules from the shotgun utils framework
shotgun_model = tank.platform.import_framework("tk-framework-shotgunutils", "shotgun_model")
shotgun_view = tank.platform.import_framework("tk-framework-shotgunutils", "shotgun_view")


class ExampleDelegate(shotgun_view.WidgetDelegate):
    """
    Shows an example how to use a shotgun_view.ListWidget in a std view.
    """

    def __init__(self, view):
        """
        Constructor
        """
        shotgun_view.WidgetDelegate.__init__(self, view)
        
    def _create_widget(self, parent):
        """
        Returns the widget to be used when creating items
        """
        return shotgun_view.ListWidget(parent)
    
    def _on_before_paint(self, widget, model_index, style_options):
        """
        Called when a cell is being painted.
        """        
        # extract the standard icon associated with the item
        icon = model_index.data(QtCore.Qt.DecorationRole)
        thumb = icon.pixmap(512)
        widget.set_thumbnail(thumb)

        # get the shotgun query data for this model item     
        sg_item = shotgun_model.get_sg_data(model_index)   

        # get values and populate widget
        version_str = "Version %03d" % sg_item.get("version_number")            
        created_str = sg_item.get("created_at")            
        desc_str = sg_item.get("description")        
        author_str = "%s" % sg_item.get("created_by").get("name")
        
        header_str = "<b>%s</b>" % (version_str)
        body_str = "<b>%s</b> &mdash; %s<br><br><small>%s</small>" % (author_str, desc_str, created_str)
        widget.set_text(header_str, body_str)

    def _on_before_selection(self, widget, model_index, style_options):
        """
        Called when a cell is being selected.
        """
        # do std drawing first
        self._on_before_paint(widget, model_index, style_options)        
        widget.set_selected(True)        
            
    def sizeHint(self, style_options, model_index):
        """
        Base the size on the icon size property of the view
        """
        return shotgun_view.ListWidget.calculate_size()
             

The next section contains the detailed API reference for the delegate system.

Class WidgetDelegate API Reference

Convenience wrapper that makes it straight forward to use widgets inside of delegates. This class is basically an adapter which lets you connect a view (QAbstractItemView) with a QWidget of choice. This widget is used to "paint" the view when it is being rendered. This class can be used in conjunction with the various widgets found as part of the framework module (for example list_widget and thumb_widget). You use this class by subclassing it and implementing the three methods _create_widget(), _on_before_paint(), _on_before_selection() and sizeHint().

Note! In order for this class to handle selection correctly, it needs to be attached to the view after the model has been attached. (This is to ensure that it is able to obtain the view's selection model correctly.)

WidgetDelegate Constructor

Class constructor.

Constructor Parameters

  • QWidget view - The view upon which the delegate should operate.

_create_widget()

This needs to be implemented by any deriving classes. Should return a QWidget that will be used when grid cells are drawn.

delegate_obj._create_widget( parent )

Parameters & Return Value

  • QWidget parent - QWidget to parent the widget to.
  • Returns: QWidget that will be used to paint grid cells in the view.

_on_before_paint()

This needs to be implemented by any deriving classes. This is called just before a cell is painted. This method should configure values on the widget (such as labels, thumbnails etc) based on the data contained in the model index parameter which is being passed.

delegate_obj._on_before_paint( widget, model_index, style_options )

Parameters & Return Value

  • QWidget widget - The QWidget (constructed in _create_widget()) which will be used to paint the cell.
  • QModelIndex index - QModelIndex object representing the data of the object that is about to be drawn.
  • QStyleOptionViewItem style_options - QStyleOptionViewItem object containing specifics about the view related state of the cell.

_on_before_selection()

This needs to be implemented by any deriving classes. This method is called just before a cell is selected. This method should configure values on the widget (such as labels, thumbnails etc) based on the data contained in the model index parameter which is being passed.

delegate_obj._on_before_selection( widget, model_index, style_options )

Parameters & Return Value

  • QWidget widget - The QWidget (constructed in _create_widget()) which will be used to paint the cell.
  • QModelIndex index - QModelIndex object representing the data of the object that is about to be drawn.
  • QStyleOptionViewItem style_options - QStyleOptionViewItem object containing specifics about the view related state of the cell.

Class ListWidget and ThumbWidget API Reference

The ListWidget and ThumbWidget classes are simple standard building blocks that you can use in your views to present lists with thumbnails and action menus. They are suitable for horizontally scrolling collections of items such as Shots, Assets or Notes.

ListWidget/ThumbWidget Constructor

Class constructor.

Constructor Parameters

  • QObject parent - Parent object.

set_actions()

Adds a list of QActions to add to the actions menu for this widget.

widget_obj.set_actions( actions )

Parameters & Return Value

  • list actions - List of QActions to be added to this widget's action menu

set_selected()

Adjust the style sheet to indicate selection or not

widget_obj.set_selected( selected )

Parameters & Return Value

  • bool selected - Indicates if the widget should appear to be selected or not.

set_thumbnail()

Assign a thumbnail to the widget. The pixmap must be 100x100 or it will appear squeezed.

widget_obj.set_thumbnail( pixmap )

Parameters & Return Value

  • QPixmap pixmap - Icon to assign.

set_text()

Populates the widget text

widget_obj.set_text( header, body )

Parameters & Return Value

  • str header - Header text
  • str body - Body text

calculate_size()

Returns the size suitable for this widget.

widget_obj.calculate_size()

Parameters & Return Value

  • Returns: QSize value.

Help Popup Window

The help popup is a window which presents a series of help images to the user, typically displayed at startup to help introduce new feature or a new application to a user. The information is presented as an interactive slide show where the user can easily navigate between sides.

You simply provide it with a list of transparent bitmaps sized 650x400 and it will create a slideshow with animated transitions and a link to the bundle's associated documentation (retrieved from the manifest).

The help_screen module contains a single method that takes care of everything. Simply create the number of 650x400 images that you want and export them as transparent pngs and ensure that the background is transparent (this is not a strict requirement, but it will most likely look strange with a non-transparent background). Add the images to the QT resource section of your app and then execute the following code to launch the help screen:

# example of how the help screen can be used within your app code

# import the module - note that this is using the special
# import_framework code so it won't work outside an app
help_screen = sgtk.platform.import_framework("tk-framework-shotgunutils", "help_screen")

# generate pixmaps of the help screen resources we want to display
help_pix = [ QtGui.QPixmap(":/res/help_1.png"), 
             QtGui.QPixmap(":/res/help_2.png"), 
             QtGui.QPixmap(":/res/help_3.png"),
             QtGui.QPixmap(":/res/help_4.png") ] 

# get the current app object
app = sgtk.platform.current_bundle()

# get the current QT UI window 
window = current_dialog_object.window()

# and present the help screen. This is a non-blocking call 
# and the application flow will continue
help_screen.show_help_screen(self.window(), app, help_pix)

Settings module

The settings module makes it easy to store things like user preferences, app related state etc. For example, if you want your app to remember the state of a checkbox across sessions, you can use the settings module to store this value. Adding persisting settings to an app can quickly drastically improve the user experience at a very low cost.

This settings module wraps around QSettings. This means that the settings data will be stored on your local machine and that they are for the current user only. If you need to share a preference or setting between multiple users, this module is most likely not the right one to use.

Settings can have different scope. This indicates how the setting should be shared between instances of apps, shotgun setups etc. Please note that the setting is still per-user and per-machine, so if it is scoped to be "global", it means that it will be shared across all different apps, engines, configurations, projects and shotgun sites for the current user on their local machine.

  • SCOPE_GLOBAL - No restriction.
  • SCOPE_SITE - Settings are per Shotgun site.
  • SCOPE_PROJECT - Settings are per Shotgun project.
  • SCOPE_CONFIG - Settings are per Shotgun Pipeline Configuration.
  • SCOPE_INSTANCE - Settings are per app or engine instance. For example, if your app contains a set of filters, and you want these to be remembered across sessions, you would typically use this scope. Each instance of the app will remember its own filters, so when you run it in the asset environment, one set of filters are remembered, when you run it in the shot environment, another set of filters etc.
  • SCOPE_ENGINE - One setting per engine. This makes it possible to store one set of preferences for apps running in Photoshop, Maya, Nuke etc. This makes it possible to for example store a setting that remembers if a "welcome screen" for your app has been displayed - so that it is only displayed once in Maya, once in Nuke etc.

The following code illustrates typical use of the settings module:

# example of how the settings module can be used within your app code
# import the module - note that this is using the special
# import_framework code so it won't work outside an app
settings = sgtk.platform.import_framework("tk-framework-shotgunutils", "settings")

# typically in the constructor of your main dialog or in the app, create a settings object:
self._settings_manager = settings.UserSettings(sgtk.platform.current_bundle())

# the settings system will handle serialization and management of data types
# so you can pass simple types such as strings, ints and lists and dicts of these.
#
# retrieve a settings value and default to a value if no settings was found
scale_val = self._settings_manager.retrieve("thumb_size_scale", 140)

# or store the same value
self._settings_manager.store("thumb_size_scale", 140)

# by default, things are scoped with `SCOPE_GLOBAL`. 
# If you want to specify another scope, add a scope parameter.

# Fetch a prefeence with a specific scope
ui_launched = self._settings_manager.retrieve("ui_launched", False, self._settings_manager.SCOPE_ENGINE)

# And store a preference with a specific scope
self._settings_manager.store("ui_launched", True, self._settings_manager.SCOPE_ENGINE)

Progress Overlay

The progress overlay module provides a standardized progress overlay widget which can easily be placed on top of any other QWidget to indicate that work is happening and potentially report messages back to the user. Once you have instantiated and placed it on top of another widget, you can execute various methods to control its state.

# example of how the overlay can be used within your app code

# import the module - note that this is using the special
# import_framework code so it won't work outside an app
overlay = sgtk.platform.import_framework("tk-framework-shotgunutils", "overlay_widget") 

# now inside your app constructor, create an overlay and parent it to something
self._overlay = overlay.ShotgunOverlayWidget(my_widget)

# now you can use the overlay to report things to the user
try:
   self._overlay.start_spin()
   run_some_code_here()
except Exception, e:
   self._overlay.show_error_message("An error was reported: %s" % e)
finally:
   self._overlay.hide()

Please note that the example above is crude and for heavy computational work we recommend an asynchronous approach with a worker thread for better UI responsiveness.

API Reference

ShotgunOverlayWidget Constructor

ShotgunOverlayWidget(parent)

Constructor Parameters

  • QWidget parent - Parent object.

start_spin()

Show a spinner animation.

overlay_obj.start_spin()

Parameters & Return Value

  • QWidget parent_widget - A QWidget object on top of which any progress overlays will be rendered.

show_error_message()

Display an error message centered on the overlay.

overlay_obj.show_error_message(msg)

Parameters & Return Value

  • str msg - Error message to display

show_message()

Display a message centered on the overlay.

overlay_obj.show_message(msg)

Parameters & Return Value

  • str msg - Message to display

show_message_pixmap()

Display a pixmap centered on the overlay

overlay_obj.show_message_pixmap(pixmap)

Parameters & Return Value

  • QPixmap pixmap - Pixmap to display

hide()

Hides the overlay.

overlay_obj.hide(hide_errors=True)

Parameters & Return Value

  • bool hide_errors - If this flag is set to false, the hide operation will only hide the overlay in case no errors have been reported.

Shotgun Data Retriever

The Shotgun data retriever makes it easy to pull in shotgun data and thumbnails in a background thread. It also manages caching of thumbnails on disk so that they don't need to be retrieved over and over again.

You start the worker thread, then you can submit a series of requests which will be handled by the data object. Each time data arrives, a signal is emitted with the details of the data. Each object will by default have its own Shotgun API connection. Requests are prioritised so according to their priority. For example, find() queries will always take precedence over shotgun thumbnail downloads.

# import the module - note that this is using the special
# import_framework code so it won't work outside an app
sg_data = sgtk.platform.import_framework("tk-framework-shotgunutils", "shotgun_data") 

class ExampleWidget(QtGui.QWidget):

    def __init__(self):

        QtGui.QWidget.__init__(self)
                
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        
        # set up data retriever
        self.__sg_data = sg_data.ShotgunDataRetriever(self)
        self.__sg_data.work_completed.connect( self.__on_worker_signal)
        self.__sg_data.work_failure.connect( self.__on_worker_failure)

        # and start its thread!
        self.__sg_data_retriever.start()

        # do an async request
        self._find_uid = self.__sg_data.execute_find("Shot", [], ["code"])


    def closeEvent(self, event):
        """
        Executed when the widget dialog is closed.
        """
        # gracefully stop our data retriever. This call
        # will block util the currently processing request has completed.
        self.__sg_data_retriever.stop()

        # okay to close dialog
        event.accept()

    def __on_worker_failure(self, uid, msg):
        """
        Asynchronous callback - the worker thread errored.
        """        
        print "Error: %s" % msg        
     
    def __on_worker_signal(self, uid, request_type, data):
        """
        Signaled whenever the worker completes something.
        """
        print "Data arrived: %s" % data 

API Reference for ShotgunDataRetriever

Asynchronous data retrieve class which can be used to retrieve data and thumbnails from Shotgun and from disk thumbnail cache. The class manages a queue where you can add various requests. Requests are queued up using the execute_find() and request_thumbnail() methods.

Requests are executed in the following priority order:

  • First any thumbnails that are already cached on disk are handled.
  • Next, shotgun find() queries are handled.
  • Lastly thumbnail downloads are handled.

The thread will emit work_completed and work_failure signals when tasks are completed (or fail). The clear() method will clear the current queue. The currently processing item will finish processing and may send out signals even after a clear. Make sure you call the stop() method prior to destruction in order for the system to gracefully shut down.

Signal work_completed

work_completed(uid, request_type, data_dict)

Signal that is emitted every time a requested task has completed.

  • uid is a unique id which matches the unique id returned by the corresponding request call.

  • request_type is a string denoting the type of request this event is associated with. It can be either find or thumbnail.

  • data_dict is a dictionary containing the payload of the request. It will be different depending on what type of request it is. For find() requests, the data_dict will be on the form {"sg": data }, where data is the data returned by the Shotgun API. For thumbnail requests, the data dict will be on the form {"thumb_path": path}, where path is a path to a location on disk where the thumbnail can be accessed.

Signal work_failure

work_completed(uid, error_message)

Signal that is emitted every time a requested task has failed.

  • uid is a unique id which matches the unique id returned by the corresponding request call.

  • error_message is a string which contains information about the failure.

ShotgunDataRetriever Constructor

Constructor. This will create a new thread instance. Once instantiated, you need to start the thread by executing start().

ShotgunDataRetriever(parent=None)

Constructor Parameters

  • QObject parent - Parent object.

set_shotgun_connection()

Specify the shotgun api instance this model should use to communicate with Shotgun. If not specified, each instance will instantiate its own connection, via toolkit. The behavior where each instance has its own connection is generally recommended for thread safety reasons since the Shotgun API isn't natively threadsafe.

We strongly recommend that the API instance passed in here is not used in any other threads since this may lead to undefined behaviour.

sg_data_obj.set_shotgun_connection( sg )

Parameters & Return Value

  • Shotgun sg - Shotgun API instance.

clear()

Clears the queue. Any currently processing item will complete without interruption, and signals will be sent out for these items.

sg_data_obj.destroy()

stop()

Gracefully stop the receiver.

Once stop() has been called, the object needs to be discarded. This is a blocking call. It will synchronounsly wait until any potential currently processing item has completed.

sg_data_obj.stop()

execute_find()

Add a Shotgun API find query to the queue.

The query will be queued up and once processed, either a work_completed or work_failure signal will be emitted.

sg_data_obj.execute_find(entity_type, filters, fields, order = None)

Parameters & Return Value

  • str entity_type - Shotgun entity type
  • list filters - List of find filters to pass to Shotgun find call
  • list fields - List of fields to pass to Shotgun find call
  • list order - List of order dicts to pass to Shotgun find call
  • Returns: A unique identifier string representing this request. This identifier is also part of the payload sent via the work_completed and work_failure signals, making it possible to match them up.

request_thumbnail()

Adds a Shotgun thumbnail request to the queue.

If a cached version of the thumbnail exists, this will be returned. If not, the Shotgun will be downloaded from Shotgun.

sg_data_obj.request_thumbnail(url, entity_type, entity_id, field)

Parameters & Return Value

  • str url - The thumbnail url string that is associated with this thumbnail. This is the field value as returned by a Shotgun query.

  • str entity_type - Shotgun entity type with which the thumb is associated.

  • int entity_id - Shotgun entity id with which the thumb is associated.

  • str field - Thumbnail field. Normally image but could also for example be a deep link field such as sg_sequence.Sequence.image.

  • Returns: A unique identifier string representing this request. This identifier is also part of the payload sent via the work_completed and work_failure signals, making it possible to match them up.

Clone this wiki locally