Shotgun Hierarchy Model

Introduction

The shotgun hierarchy data model helps you build responsive, data rich applications quickly, leveraging Qt’s built-in model/view framework to represent your production hierarchy.

_images/hierarchy_model.png

The Shotgun Hierarchy Model is a custom Qt Model specialized to represent your Shotgun production hierarchy as defined in your site’s Tracking Settings. The hierarchy represented by the model is dynamically constructed based on a seed_entity_field which represents the target of the hierarchy (i.e. what you’re trying to drill down to in view using the model).

Like the ShotgunModel, the ShotgunHierarchyModel uses a disk based cache and runs queries asynchronously to Shotgun in the background for performance. 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.

_images/model_inheritance.png

Why should I use the Shotgun Hierarchy Model?

Using the Shotgun Hierarchy 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 Hierarchy 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 Qt’s built-in functionality, you get access to a mature and well documented toolbox that makes it quick to develop tools:

  • A hierarchy model instance represents a single Shotgun shotgun_api3.Shotgun.nav_expand query. The model is lazy-loaded as each time you expand an item in the view, the children of that item are queried.
  • The model is cached, meaning that all data is fetched in the background in worker threads. 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 selection models, making it easy to create consistent selection behavior, 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.
  • With an attached view, you can easily control the Qt delegates system, making it easy to draw custom UIs for each item.

Shotgun Hierarchy 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 shotgunutils framework
shotgun_model = sgtk.platform.import_framework(
    "tk-framework-shotgunutils", "shotgun_model")

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

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

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

# build a hierarchy that encompases all your projects
# targeting entities linked to the "entity" field on "Version" entities
model.load_data("Version.entity")

The above code will create a standard Qt tree view for your site (all projects) drilling down to entities with linked versions.

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 ShotgunHierarchyModel. This makes it possible to customize the data as it arrives from Shotgun and how it will be displayed by the view.
  • If you want to retrieve results from your view, connect signals to the view’s selection model. The items in the view hold all the information you need to query all the target entities under that level of the hierarchy. For example, an item that represents a Sequence entity in a view stores all the required information for querying target entities for all of its Shots.
  • 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 which is part of the Qt widgets framework. For more information, see WidgetDelegate

SimpleShotgunHierarchyModel

Convenience wrapper around the Shotgun Hierarchy Model for quick and easy access. Use this when you want to prototype data modeling or if your are looking for a simple hierarchy without any customization. All you need to do is to instantiate the class (typically once, in your constructor) and then call SimpleShotgunHierarchyModel.load_data() to specify the type of hierarchy to build in the model. Subsequently, call load_data() whenever you wish to change the hierarchy represented by the model.

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

class shotgun_model.SimpleShotgunHierarchyModel(parent, schema_generation=0, bg_task_manager=None, include_root=None)[source]

Bases: shotgun_model.shotgun_hierarchy_model.ShotgunHierarchyModel

Convenience wrapper around the Shotgun Hierarchy model for quick and easy access.

All you need to do is to instantiate the class (typically once, in your constructor) and then call load_data() to specify which shotgun shotgun_api3.Shotgun.nav_expand query to load up the top-level items in the hierarchy. The remaining items will be queried asynchronously as items are expanded.

Subsequently call load_data() whenever you wish to change the shotgun_api3.Shotgun.nav_expand query associated with the model.

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

Initialize the Hierarcy model.

Parameters:
  • parent (QObject) – The model’s parent.
  • schema_generation – Schema generation number. Advanced parameter. If your shotgun model contains logic in subclassed methods that modify the shotgun data prior to it being put into the cache system that the ShotgunModel maintains, you can use this option to ensure that different versions of the code access different caches. If you change your custom business logic around and update the generation number, both new and old versions of the code will work correctly against the cached data.
  • bg_task_manager (BackgroundTaskManager) – Background task manager to use for any asynchronous work. If this is None then a task manager will be created as needed.
  • include_root (str) – Defines the name of an additional, top-level model item that represents the root. In views, this item will appear as a sibling to top-level children of the root. This allows for UX whereby a user can select an item representing the root without having a UI that shows a single, top-level item. An example would be displaying published file entity hierarchy with top level items: “Assets”, “Shots”, and “Project Publishes”. In this example, the supplied arg would look like: include_root="Project Publishes". If include_root is None, no root item will be added.
load_data(seed_entity_field, root=None, entity_fields=None)[source]

Loads shotgun data into the model, using the cache if possible.

Parameters:
  • seed_entity_field (str) –

    This is a string that corresponds to the field on an entity used to seed the hierarchy. For example, a value of Version.entity would cause the model to display a hierarchy where the leaves match the entity value of Version entities.

    NOTE: This value is currently limited to either Version.entity or PublishedFile.entity

  • root (dict) – This is the entity that will be at the root of the hierarchy view. By default, this value is None, which means the root of the hierarchy will be at the site level. Only projects can be set as the root of a hierarchy model.
  • entity_fields (dict) – A dictionary that identifies what fields to include on returned entities. Since the hierarchy can include any entity structure, this argument allows for specification of additional fields to include as these entities are returned. The dict’s keys correspond to the entity type and the value is a list of field names to return.

ShotgunHierarchyModel

A Qt Model representing a Shotgun query.

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

class shotgun_model.ShotgunHierarchyModel(parent, schema_generation=0, bg_task_manager=None, include_root=None)[source]

A Qt Model representing a Shotgun hierarchy.

Warning

Use of this model requires version Shotgun v7.0.2 or later. Attempts to construct an instance of this model on an older version of Shotgun will result with a single item in the model saying that Hierarchy model isn’t supported. A warning will also be logged.

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 QAbstractItemView of some sort which will display the result.

The model stores a single column, lazy-loaded Shotgun Hierarchy as queried via the shotgun_api3.Shotgun.nav_expand python-api method. The structure of items in the hierarchy mimics what is found in Shotgun as configured in each project’s Tracking Settings.

Signal:async_item_retrieval_completed (ShotgunHierarchyModel): Emitted when a query to ShotgunHierarchyModel.async_item_from_entity() or ShotgunHierarchyModel.async_item_from_paths() has completed.

Initialize the Hierarcy model.

Parameters:
  • parent (QObject) – The model’s parent.
  • schema_generation – Schema generation number. Advanced parameter. If your shotgun model contains logic in subclassed methods that modify the shotgun data prior to it being put into the cache system that the ShotgunModel maintains, you can use this option to ensure that different versions of the code access different caches. If you change your custom business logic around and update the generation number, both new and old versions of the code will work correctly against the cached data.
  • bg_task_manager (BackgroundTaskManager) – Background task manager to use for any asynchronous work. If this is None then a task manager will be created as needed.
  • include_root (str) – Defines the name of an additional, top-level model item that represents the root. In views, this item will appear as a sibling to top-level children of the root. This allows for UX whereby a user can select an item representing the root without having a UI that shows a single, top-level item. An example would be displaying published file entity hierarchy with top level items: “Assets”, “Shots”, and “Project Publishes”. In this example, the supplied arg would look like: include_root="Project Publishes". If include_root is None, no root item will be added.

Loading the Hierarchy Data

These methods are used by subclasses to define the Shotgun query that loads and caches the hierarchy items and refreshes them once cached.

_load_data(seed_entity_field, root=None, entity_fields=None, cache_seed=None)[source]

This is the main method to use to configure the hierarchy model. You basically pass a specific shotgun_api3.Shotgun.nav_expand query to the model and it will start tracking this particular set of parameters.

Any existing data 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.

Parameters:
  • seed_entity_field (str) –

    This is a string that corresponds to the field on an entity used to seed the hierarchy. For example, a value of Version.entity would cause the model to display a hierarchy where the leaves match the entity value of Version entities.

    NOTE: This value is currently limited to either Version.entity or PublishedFile.entity

  • root (dict) – This is the entity that will be at the root of the hierarchy view. By default, this value is None, which means the root of the hierarchy will be at the site level. Only projects can be set as the root of a hierarchy model.
  • entity_fields (dict) – A dictionary that identifies what fields to include on returned entities. Since the hierarchy can include any entity structure, this argument allows for specification of additional fields to include as these entities are returned. The dict’s keys correspond to the entity type and the value is a list of field names to return.
  • cache_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 hierarchy query, this seed is typically generated from the seed entity field and return entity fields supplied to this method. However, in cases where 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 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 cached data was loaded, False if not.

destroy()

Call this method prior to destroying this object.

Base implementation ensures the data worker is stopped and calls clear() on the model.

hard_refresh()

Clears any caches on disk, then refreshes the data.

is_data_cached()

Determine if the model has any cached data.

Returns:True if cached data exists for the model, False otherwise.

Customizing the Hierarchy Items

The following methods can be used by subclasses to customize the model and the information it displays when attached to a view.

_before_data_processing(data)

Called just after data has been retrieved from Shotgun but before any processing takes place.

Note

You can subclass this if you want to perform summaries, calculations and other manipulations of the data before it is passed on to the model class.

Parameters:data – a shotgun dictionary, as retunrned by a CRUD SG API call.
Returns:should return a shotgun dictionary, of the same form as the input.
_finalize_item(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.

Note

You can subclass this if you want to run post processing on the data as it is arriving. For example, if you are showing a list of task statuses in a filter view, you may want to remember which statuses a user had checked and unchecked the last time he was running the tool. By subclassing this method you can easily apply those settings before items appear in the UI.

Parameters:itemQStandardItem that is about to be added to the model. This has been primed with the standard settings that the ShotgunModel handles.
_item_created(item)[source]

Called when an item is created, before it is added to the model.

Warning

This base class implementation must be called in any subclasses overriding this behavior. Failure to do so will result in unexpected behavior.

This base class implementation handles setting the foreground color of the item if it has no associated entities.

Parameters:item (QStandardItem) – The item that was just created.
_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.

Note

You can subclass this to add custom data to the model in a very flexible fashion. If you for example are loading published files from Shotgun, you could use this to load up a listing of files on disk, resulting in a model that shows both published files and local files. External data will not be cached by the ShotgunModel framework.

Returns:list of QStandardItem
_populate_item(item, sg_data)

Whenever an item is downloaded from Shotgun, 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.

Parameters:
  • itemQStandardItem that is about to be added to the model.
  • sg_data – Shotgun data dictionary that was received from Shotgun.
_populate_default_thumbnail(item)[source]

Sets the icon for the supplied item based on its “kind” as returned by the shotgun_api3.Shotgun.nav_expand api call.

Parameters:item – The QStandardItem item to set the icon for.
_set_tooltip(item, data)

Sets the tooltip for the supplied item.

Called when an item is created.

Parameters:
  • item – Shotgun model item that requires a tooltip.
  • data – Dictionary of the SG data associated with the model.

Instance Methods

item_from_path(path)[source]

Returns a QStandardItem for the supplied path.

Returns None if not found.

Parameters:path (str) – The path to search the tree for. The paths match those used by and returned from the python-api’s shotgun_api3.Shotgun.nav_expand method.
Returns:QStandardItem or None if not found
async_item_from_entity(entity)[source]

Asynchronously loads an entity’s node and all its parents and emits a signal with the associated ShotgunHierarchyItem when the node is loaded.

Parameters:entity (dict) – Entity dictionary with keys type and id.
Signals:async_item_retrieval_completed
async_item_from_paths(paths)[source]

Takes a list of paths that incrementally dig deeper into the model and signals when the node is found and loaded in memory.

Parameters:list(str) – List of paths from the nav api that drill down further and further into the tree.
Signals:async_item_retrieval_completed