Apps, Engines and Frameworks
This part of the API documentation covers all the classes and methods used when dealing with Engines, Apps and Frameworks. If you are interested in developing your own apps, engines or frameworks, the base classes needed to be derived from are outlined below. The documentation also covers how to initialize and shut down the Toolkit engine platform.
Note
In order to use the functionality of sgtk.platform
, you need to run it
from an initialized toolkit environment. For more information on how to
set this up, see Initialization and startup.
Managing Engines and Apps
The methods in this section are used when you want to start up or manage a Toolkit engine. This typically happens directly once a host application (e.g. Maya or Nuke) has been launched. A running engine typically needs to be terminated before a new engine can be started. The method for terminating an engine can be found on the engine class itself. Methods defining a standard interface for launching host applications are also provided.
- sgtk.platform.start_engine(engine_name, tk, context)[source]
Creates an engine and makes it the current engine. Returns the newly created engine object. Example:
>>> import sgtk >>> tk = sgtk.sgtk_from_path("/studio/project_root") >>> ctx = tk.context_empty() >>> engine = sgtk.platform.start_engine('tk-maya', tk, ctx) >>> engine <Sgtk Engine 0x10451b690: tk-maya, env: shotgun>
Note
This is for advanced workflows. For standard use cases, use
bootstrap_engine()
. For more information, see Initialization and startup.- Parameters:
- Returns:
Engine
instance- Raises:
TankEngineInitError
if an engine could not be started for the passed context.
- sgtk.platform.current_engine()[source]
Returns the currently active engine.
- Returns:
Engine
instance or None if no engine is running.
- sgtk.platform.get_engine_path(engine_name, tk, context)[source]
Returns the path to the engine corresponding to the given engine name or None if the engine could not be found.
Similar to
start_engine()
, but instead of starting an engine, this method returns the path to a suitable engine. This helper method is sometimes useful when initializing engines for applications that do not have a built in python interpreter.Example:
>>> import sgtk >>> tk = sgtk.sgtk_from_path("/studio/project_root") >>> ctx = tk.context_empty() >>> sgtk.platform.get_engine_path('tk-maya', tk, ctx) /studio/sgtk/install/engines/app_store/tk-maya/v0.1.0
- sgtk.platform.find_app_settings(engine_name, app_name, tk, context, engine_instance_name=None)[source]
Utility method to find the settings for an app in an engine in the environment determined for the context by pick environment hook.
- Parameters:
- Returns:
list of dictionaries containing the engine name, application name and settings for any matching applications that are found and that have valid settings
- sgtk.platform.change_context(new_context)[source]
Running change_context will attempt to change the context the engine and its apps are running in on the fly. The current engine must accept the context change, otherwise a full restart of the engine will be run instead.
The determination of whether an engine supports context changing comes from its “context_change_allowed” property. If that property returns True, then the context change will be allowed to proceed. If it returns False, then the engine’s “change_context” method will raise
TankContextChangeNotSupportedError
, which will then trigger a restart of the engine and all of its apps.In the event that the engine does support context changes, any apps that support context changing will do so, as well. Any that do not will themselves be restarted within the new context.
The benefit of supporting context changes in engines and apps is speed. The end result of this routine should be identical to that of a restart, but will require less time to complete.
For more information on supporting context changing, see the following:
- Parameters:
new_context (
Context
) – The new Context to change to.
- sgtk.platform.restart(new_context=None)[source]
Restarts the currently running Toolkit platform. This includes reloading all configuration files as well as reloading the code for all apps and engines. (The Core API, however, is not reloaded). The call does not take any parameters and does not return any value.
Any open windows will remain open and will use the old code base and settings. In order to access any changes that have happened as part of a reload, you need to start up new app windows (typically done via the Shotgun menu) and these will use the fresh code and configs.
- Parameters:
new_context (
Context
) – The new Context to start the engine in, if desired. Default behavior is to restart the engine with its current context.
Engines
A Toolkit engine connects a runtime environment such as a DCC with the rest of the Toolkit ecosystem. As the engine starts up, it loads the various associated apps and frameworks defined in the configuration and acts as a host for all these objects, ensuring that they can operate in a consistent fashion across integrations.
Information for App Developers
If you are developing an app, you typically call out to the engine via the Application.engine()
accessor.
You use the engine for a couple of main things:
Dialog UI creation via
Engine.show_dialog()
,Engine.show_modal()
orEngine.show_panel()
Command registration via
Engine.register_command()
The engine acts as a bridge between the DCC and the App so that the app doesn’t have to
contain DCC-specific code to create dialogs or manage menus etc. Typically, any DCC specific code
is contained within a Hook
, making it easy to design apps that
can be extended easily to support new engine environments.
Information for Engine developers
The engine is a collection of files, similar in structure to an App. It has an engine.py
file and this must
derive from the Engine
Base class. Different engines then re-implement various aspect of this base
class depending on their internal complexity. A summary of functionality include:
The base class exposes various init and destroy methods which are executed at various points in the startup process. These can be overridden to control startup and shutdown execution.
The engine provides a commands dictionary containing all the command objects registered by apps. This is typically accessed when menu entries are created.
Methods for displaying UI dialogs and windows can be overridden if the way the engine runs QT does not the default base class behavior.
The typical things an engine needs to handle are:
Menu management. At engine startup, once the apps have been loaded, the engine needs to create its Flow Production Tracking menu and add the various apps to this menu.
Logging methods are typically overridden to write to the application log.
UI methods are typically overridden to ensure seamless integration of Windows launched by Toolkit apps and the underlying host application window management setup. Engines are launched via the
stgk.platform.start_engine()
command. This command will read the configuration files, launch the engines, load all apps etc. The goal with the engine is that once it has launched, the it provides a consistent python/QT interface to the apps. Since all engines implement the same base class, apps can call methods on the engines to for example create UIs. It is up to each engine to implement these methods so that they work nicely inside the host application.An interface to startup DCC applications that centralizes the business logic of discovering executable paths, setting a proper environment for launch, and initializing toolkit integration during the launch phase. This is described in detail in the Launching Software section.
Engine Events
Engines have the ability to emit events that can then be handled by Toolkit Apps. The design follows closely that of Qt’s events, where event handler methods can be implemented in a Toolkit App to execute custom behavior tied to specific event types.
Emitting Engine Events
It is an engine’s responsibility to monitor its host DCC application’s event or signalling frameworks to then emit its associated event. The example below is listening for a Qt signal from a host’s “frontend” handle and then emits a FileOpenEvent:
def pre_app_init(self):
"""
Runs before apps are initialized.
"""
frontend.file_opened.connect(self._handle_file_open)
def _handle_file_open(self, file_path):
"""
Emits a file open event that apps can listen for.
:param str file_path: The path of the file opened.
"""
event = sgtk.platform.events.FileOpenEvent(file_path)
self.log_debug("Emitting event %s..." % event)
self._emit_event(event)
Handling Engine Events
Toolkit Apps can receive and handle an event in one of two ways:
Override the type specific event handler. The advantages of this approach to event handling is that there is no need to type check the event before handling it, and the slight performance benefit of only running custom logic when an event of the type you’re interested in is emitted. In the case of a file-open event, the Toolkit App’s
Application.event_file_open()
method would be reimplemented.Override the generic event handler. Every engine event that is emitted, regardless of type, will be sent to the Toolkit App’s
Application.event_engine(event)()
method. The approach to handling an engine event in this manner would be to type check the given event object usingisinstance()
and run the appropriate logic according to the results.
In the example below, the Toolkit App has reimplemented the Application.event_file_open()
method in
order to execute custom logic when the parent Engine
has indicated that a new file has been
opened by the host DCC application:
def event_file_open(self, event):
"""
Handles event notifications from the parent engine.
:param event: The event object that was emitted.
:type event: :class:`~sgtk.platform.events.FileOpenEvent`
"""
self.engine.log_debug("Handling event: %s" % event)
self.set_version_entity_by_file(file_path=event.file_path)
Engine
- class sgtk.platform.Engine(tk, context, engine_instance_name, env)[source]
Base class for an engine. When a new DCC integration is created, it should derive from this class.
Engine instances are constructed by the toolkit launch process and various factory methods such as
start_engine()
.- Parameters:
Engine Customizations
The following methods can be used by subclasses to customize engine behavior.
- _create_dialog(title, bundle, widget, parent)[source]
Create a TankQDialog with the specified widget embedded. This also connects to the dialogs dialog_closed event so that it can clean up when the dialog is closed.
Note
For more information, see the documentation for
show_dialog()
.- Parameters:
title – The title of the window
bundle – The app, engine or framework object that is associated with this window
widget (
PySide.QtGui.QWidget
) – A QWidget instance to be embedded in the newly created dialog.
- _create_dialog_with_widget(title, bundle, widget_class, *args, **kwargs)[source]
Convenience method to create an sgtk TankQDialog with a widget instantiated from widget_class embedded in the main section.
Note
For more information, see the documentation for
show_dialog()
.- Parameters:
title – The title of the window
bundle – The app, engine or framework object that is associated with this window
widget_class (
PySide.QtGui.QWidget
) – The class of the UI to be constructed. This must derive from QWidget.
Additional parameters specified will be passed through to the widget_class constructor.
- _create_widget(widget_class, *args, **kwargs)[source]
Create an instance of the specified widget_class. This wraps the widget_class so that the TankQDialog it is embedded in can connect to it more easily in order to handle the close event.
When overriding in a derived engine, be sure to call the base implementations of
_create_widget()
and_create_dialog()
to ensure that all dialogs and widgets are tracked efficiently and safely.Note
For more information, see the documentation for
show_dialog()
.- Parameters:
widget_class (
PySide.QtGui.QWidget
) – The class of the UI to be constructed. This must derive from QWidget.
Additional parameters specified will be passed through to the widget_class constructor.
- _define_qt_base()[source]
This will be called at initialisation time and will allow a user to control various aspects of how QT is being used by Tank. The method should return a dictionary with a number of specific keys, outlined below.
qt_core - the QtCore module to use
qt_gui - the QtGui module to use
wrapper - the Qt wrapper root module, e.g. PySide
dialog_base - base class for to use for Tank’s dialog factory
- Returns:
dict
- _emit_event(event)[source]
Called by the engine whenever an event is to be emitted to child apps of this engine.
Note
Events will be emitted and child apps notified immediately.
Warning
Some event types might be triggered quite frequently. Apps that react to events should do so in a way that is aware of the potential performance impact of their actions.
- Parameters:
event (
EngineEvent
) – The event object that will be emitted.
- _emit_log_message(handler, record)[source]
Called by the engine whenever a new log message is available. All log messages from the toolkit logging namespace will be passed to this method.
Note
To implement logging in your engine implementation, subclass this method and display the record in a suitable way - typically this means sending it to a built-in DCC console. In addition to this, ensure that your engine implementation does not subclass the (old)
Engine.log_debug()
,Engine.log_info()
family of logging methods.For a consistent output, use the formatter that is associated with the log handler that is passed in. A basic implementation of this method could look like this:
# call out to handler to format message in a standard way msg_str = handler.format(record) # display message print msg_str
Warning
This method may be executing called from worker threads. In DCC environments, where it is important that the console/logging output always happens in the main thread, it is recommended that you use the
async_execute_in_main_thread()
to ensure that your logging code is writing to the DCC console in the main thread.- Parameters:
handler (
LogHandler
) – Log handler that this message was dispatched fromrecord (
LogRecord
) – Std python logging record
- _ensure_core_fonts_loaded()[source]
Loads the Shotgun approved fonts that are bundled with tk-core.
This method ensures that the Shotgun approved fonts bundled with core are loaded into Qt’s font database. This allows them to be used by apps for a consistent look and feel.
If a QApplication exists during engine initialization, it is not necessary to call this method. Similarly, subclasses that make use of core’s bundled dark look and feel will have the bundled fonts loaded automatically.
This method can/should be called by subclasses that meet the following criteria:
Create their own
QApplication
instance after engine initDo not use the bundled dark look and feel.
Have overridden
Engine._create_dialog()
.
- _get_dialog_parent()[source]
Get the QWidget parent for all dialogs created through
show_dialog()
show_modal()
.Can be overriden in derived classes to return the QWidget to be used as the parent for all TankQDialog’s.
- Returns:
QT Parent window (
PySide.QtGui.QWidget
)
- _initialize_dark_look_and_feel()[source]
Initializes a standard toolkit look and feel using a combination of QPalette and stylesheets.
If your engine is running inside an environment which already has a dark style defined, do not call this method. The Toolkit apps are designed to work well with most dark themes.
However, if you are for example creating your own QApplication instance you can execute this method to put the session into Toolkit’s standard dark mode.
This will initialize the plastique style (for Qt4) or the fusion style (for Qt5/Qt6), and set it up with a standard dark palette and supporting stylesheet.
Qt5 setStyle documentation Qt6 setStyle documentation
Apps and UIs can then extend this further by using further css.
Due to restrictions in QT, this needs to run after a QApplication object has been instantiated.
- _on_dialog_closed(dlg)[source]
Called when a dialog created by this engine is closed.
- Parameters:
dlg (
PySide.QtGui.QWidget
) – The dialog being closed
Derived implementations of this method should be sure to call the base implementation
Instance Methods & Properties
- get_metrics_properties()[source]
Returns a dictionary with properties to use when emitting a metric event for this engine.
The dictionary contains information about this engine: its name and version, and informations about the application hosting the engine: its name and version:
{ 'Host App': 'Maya', 'Host App Version': '2017', 'Engine': 'tk-maya', 'Engine Version': 'v0.4.1', }
- Returns:
A dictionary with metrics properties as per above.
- property shotgun
Returns a Shotgun API handle associated with the currently running environment. This method is a convenience method that calls out to
shotgun()
.- Returns:
Shotgun API handle
- property environment
A dictionary with information about the environment.
- Returns:
dictionary with keys
name
,description
anddisk_location
.
- property instance_name
The instance name for this engine. The instance name is the entry that is defined in the environment file.
- Returns:
instance name as string, e.g.
tk-maya
- property apps
Dictionary of apps associated with this engine
- Returns:
dictionary with keys being app name and values being app objects
- property commands
A dictionary representing all the commands that have been registered by apps in this engine via
register_command()
. Each dictionary item contains the following keys:callback
- function pointer to function to execute for this commandproperties
- dictionary with free form options - these are typically engine specific and driven by convention.
- Returns:
commands dictionary, keyed by command name
- property panels
Panels which have been registered with the engine via the
register_panel()
method. Returns a dictionary keyed by panel unique ids. Each value is a dictionary with keyscallback
andproperties
.Returns all the panels which have been registered with the engine.
- Returns:
A dictionary keyed by panel unique ids. Each value is a dictionary with keys ‘callback’ and ‘properties’
- property has_ui
Indicates that the host application that the engine is connected to has a UI enabled. This always returns False for some engines (such as the shell engine) and may vary for some engines, depending if the host application for example is in batch mode or UI mode.
- Returns:
boolean value indicating if a UI currently exists
- property has_qt5
Indicates that the host application has access to Qt 5 and that the
sgtk.platform.qt5
module has been populated with the Qt 5 modules and information.- Returns bool:
boolean value indicating if Qt 5 is available.
- property has_qt6
Indicates that the host application has access to Qt 6 and that the
sgtk.platform.qt6
module has been populated with the Qt 6 modules and information.- Returns bool:
boolean value indicating if Qt 6 is available.
- property metrics_dispatch_allowed
Indicates this engine will allow the metrics worker threads to forward the user metrics logged via core, this engine, or registered apps to PTR.
- Returns:
boolean value indicating that the engine allows user metrics to be forwarded to PTR.
- property created_qt_dialogs
A list of dialog objects that have been created by the engine.
- Returns:
A list of TankQDialog objects.
- property host_info
Returns information about the application hosting this engine.
This should be re-implemented in deriving classes to handle the logic specific to the application the engine is designed for.
A dictionary with at least a “name” and a “version” key should be returned by derived implementations, with respectively the host application name and its release string as values, e.g.
{ "name": "Maya", "version": "2017.3"}
.- Returns:
A
{"name": "unknown", "version" : "unknown"}
dictionary.
- property register_toggle_debug_command
Indicates whether the engine should have a toggle debug logging command registered during engine initialization.
- Return type:
- pre_app_init()[source]
Sets up the engine into an operational state. Executed by the system and typically implemented by deriving classes. This method called before any apps are loaded.
- post_app_init()[source]
Executed by the system and typically implemented by deriving classes. This method called after all apps have been loaded.
- destroy()[source]
Destroy all apps, then call destroy_engine so subclasses can add their own tear down code.
Note
This method should not be subclassed. Instead, implement
destroy_engine()
.
- destroy_engine()[source]
Called when the engine should tear down itself and all its apps. Implemented by deriving classes.
- change_context(new_context)[source]
Called when the engine is being asked to change contexts. This will only be allowed if the engine explicitly suppose on-the-fly context changes by way of its context_change_allowed property. Any apps that do not support context changing will be restarted instead. Custom behavior at the engine level should be handled by overriding one or both of pre_context_change and post_context_change methods.
- Parameters:
new_context (
Context
) – The context to change to.
- show_busy(title, details)[source]
Displays or updates a global “busy window” tied to this engine. The window is a splash screen type window, floats on top and contains details of what is currently being processed.
This method pops up a splash screen with a message and the idea is that long running core processes can use this as a way to communicate their intent to the user and keep the user informed as slow processes are executed. If the engine has a UI present, this will be used to display the progress message. If the engine does not have UI support, a message will be logged. The UI always appears in the main thread for safety.
Only one global progress window can exist per engine at a time, so if you want to push several updates one after the other, just keep calling this method.
When you want to remove the window, call
clear_busy()
.Note! If you are calling this from the Core API you typically don’t have access to the current engine object. In this case you can use the convenience method
tank.platform.engine.show_global_busy()
which will attempt to broadcast the request to the currently active engine.- Params title:
Short descriptive title of what is happening
- Params details:
Detailed message describing what is going on.
- clear_busy()[source]
Closes any active busy window.
For more details, see the
show_busy()
documentation.
- register_command(name, callback, properties=None)[source]
Register a
command
with a name and a callback function.A command refers to an access point for some functionality. In most cases, commands will appear as items on a Shotgun dropdown menu, but it ultimately depends on the engine - in the Shell engine, commands are instead represented as a text base listing and in the PTR desktop app it is a scrollable list of larger icons.
Note
This method is used to add menu entries for launching toolkit UIs. If you wish to register a panel UI with toolkit, you need call this method in order to register a menu command with which a user can launch the panel. In addition to this, you also need to call
register_panel()
in order to register the panel so that the engine can handle its management and persistence.An arbitrary list of properties can be passed into the engine in the form of a properties dictionary. The interpretation of the properties dictionary is engine specific, but in general the following properties are supported:
short_name
- A shorter name, typically intended for console use (e.g. ‘import_cut’)icon
- A path to a 256x256 png app icon. If not specified, the icon for the app will be used.description
- a one line description of the command, suitable for a tooltip. If no description is passed, the one provided in the app manifest will be used.title
- Title to appear on shotgun action menu (e.g. “Create Folders”)type
- The type of command - hinting at which menu the command should appear. Options vary between engines and the following are supported:context_menu
- Supported on all engines. Places an item on the context menu (first item on the shotgun menu). The context menu is a suitable location for utility items, helpers and tools.panel
- Some DCCs have a special menu which is accessible only when right clicking on a panel. Passingpanel
as the command type hints to the system that the command should be added to this menu. If no panel menu is available, it will be added to the main menu. Nuke is an example of a DCC which supports this behavior.node
- Node based applications such as Nuke typically have a separate menu system for accessing nodes. If you want your registered command to appear on this menu, use this type.
Grouping commands into collections
It is possible to group several commands into a collection. Such a collection is called a group. For example, you may have three separate commands to launch Maya 2017, Maya 2016 and Maya 2015, all under a ‘Launch Maya’ group. It is up to each engine to implement this specification in a suitable way but typically, it would be displayed as a “Launch Maya” menu with three sub menu items to represent each version of Maya.
Each group has a concept of a group default - this is what would get executed if you click on the ‘Launch Maya’ group.
To register commands with groups, pass the following two parameters in the properties dictionary:
group
- The name for a group this command should be considered a member of.group_default
- Boolean value indicating whether this command should represent the group as a whole.
Note
It is up to each engine to implement grouping and group defaults in an appropriate way. Some engines may not support grouping.
The following properties are supported for the Shotgun engine specifically:
deny_permissions
- List of permission groups to exclude this menu item for (e.g.["Artist"]
)deny_platforms
- List of platforms for which not to show the menu (e.g.["windows", "mac", "linux"]
). Please note that there are other ways to achieve this same result.supports_multiple_selection
- a special flag that allows multiple objects in Shotgun to be selected and operated on. An example showing how to write a multi select shotgun app is provided in a special branch in the sample starter app: https://github.com/shotgunsoftware/tk-multi-starterapp/tree/shotgun_multi_selectPlease note that custom icons are not supported by the Shotgun engine.
Typical usage normally looks something like this - register_command is called from the
Application.init_app()
method of an app:self.engine.register_command( "Work Area Info...", callback, {"type": "context_menu", "short_name": "work_area_info"} )
- Parameters:
name – Name of the command. This will be the key when accessed via the
commands()
dictionary.callback – Callback to call upon command execution
properties – Dictionary with command properties.
- register_panel(callback, panel_name='main', properties=None)[source]
Similar to
register_command()
, but instead of registering a menu item in the form of a command, this method registers a UI panel. A register_panel call should be used in conjunction with a register_command call.Panels need to be registered if they should persist between DCC sessions (e.g. for example ‘saved layouts’).
Just like with the
register_command()
method, panel registration should be executed from within the init phase of the app. Once a panel has been registered, it is possible for the engine to correctly restore panel UIs at startup and profile switches.Not all engines support this feature, but in for example Nuke, a panel can be added to a saved layout. Apps wanting to be able to take advantage of the persistence given by these saved layouts will need to call register_panel as part of their init_app phase.
In order to show or focus on a panel, use the
show_panel()
method instead.- Parameters:
callback – Callback to a factory method that creates the panel and returns a panel widget.
panel_name – A string to distinguish this panel from other panels created by the app. This will be used as part of the unique id for the panel.
properties – Properties dictionary. Reserved for future use.
- Returns:
A unique identifier that can be used to consistently identify the panel across sessions. This identifier should be used to identify the panel in all subsequent calls, e.g. for example
show_panel()
.
- execute_in_main_thread(func, *args, **kwargs)[source]
Execute the specified function in the main thread when called from a non-main thread. This will block the calling thread until the function returns. Note that this method can introduce a deadlock if the main thread is waiting for a background thread and the background thread is invoking this method. Since the main thread is waiting for the background thread to finish, Qt’s event loop won’t be able to process the request to execute in the main thread:
>>> from sgtk.platform.qt import QtGui >>> engine.execute_in_main_thread(QtGui.QMessageBox.information, None, "Hello", "Hello from the main thread!")
Note
This currently only works if Qt is available, otherwise it just executes immediately on the current thread.
- Parameters:
func – function to call
args – arguments to pass to the function
kwargs – named arguments to pass to the function
- Returns:
the result of the function call
- async_execute_in_main_thread(func, *args, **kwargs)[source]
Execute the specified function in the main thread when called from a non-main thread. This call will return immediately and will not wait for the code to be executed in the main thread.
Note
This currently only works if Qt is available, otherwise it just executes immediately on the current thread.
- Parameters:
func – function to call
args – arguments to pass to the function
kwargs – named arguments to pass to the function
- get_matching_commands(command_selectors)[source]
Finds all the commands that match the given selectors.
Command selector structures are typically found in engine configurations and are typically defined on the following form in yaml:
menu_favourites: - {app_instance: tk-multi-workfiles, name: Shotgun File Manager...} - {app_instance: tk-multi-snapshot, name: Snapshot...} - {app_instance: tk-multi-workfiles, name: Shotgun Save As...} - {app_instance: tk-multi-publish, name: Publish...}
Note that selectors that do not match a command will output a warning.
- Parameters:
command_selectors –
A list of command selectors, with each selector having the following structure:
{ name: command-name, app_instance: instance-name }
An empty name (“”) will select all the commands of the given instance-name.
- Returns:
A list of tuples for all commands that match the selectors. Each tuple has the format:
(instance-name, command-name, callback)
- log_debug(msg)[source]
Logs a debug message.
Deprecated since version 0.18: Use
Engine.logger()
instead.Note
Toolkit will probe for this method and use it to determine if the current engine supports the new
Engine.logger()
based logging or not. If you are developing an engine and want to upgrade it to use the new logging capabilities, you should remove the implementation oflog_debug|error|info|...()
methods and instead sublcassEngine._emit_log_message()
.- Parameters:
msg – Message to log.
- log_info(msg)[source]
Logs an info message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_warning(msg)[source]
Logs an warning message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_error(msg)[source]
Logs an error message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_exception(msg)[source]
Logs an exception message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- get_debug_tracked_qt_widgets()[source]
Returns a dictionary of debug info about created Qt dialogs and widgets.
The keys of the dictionary are the string representation of a widget and the corresponding value is a reference to that widget.
- show_dialog(title, bundle, widget_class, *args, **kwargs)[source]
Shows a non-modal dialog window in a way suitable for this engine. The engine will attempt to parent the dialog nicely to the host application. The dialog will be created with a standard Toolkit window title bar where the title will be displayed.
Note
In some cases, it is necessary to hide the standard Toolkit title bar. You can do this by adding a property to the widget class you are displaying:
@property def hide_tk_title_bar(self): "Tell the system to not show the standard toolkit toolbar" return True
Notes for engine developers
Qt dialog & widget management can be quite tricky in different engines/applications. Because of this, Sgtk provides a few overridable methods with the idea being that when developing a new engine, you only need to override the minimum amount necessary.
Making use of these methods in the correct way allows the base Engine class to manage the lifetime of the dialogs and widgets efficiently and safely without you having to worry about it.
The methods available are listed here in the hierarchy in which they are called:
show_dialog()/show_modal() _create_dialog_with_widget() _get_dialog_parent() _create_widget() _create_dialog()
For example, if you just need to make sure that all dialogs use a specific parent widget then you only need to override _get_dialog_parent() (e.g. the tk-maya engine). However, if you need to implement a two-stage creation then you may need to re-implement show_dialog() and show_modal() to call _create_widget() and _create_dialog() directly rather than using the helper method _create_dialog_with_widget() (e.g. the tk-3dsmax engine). Finally, if the application you are writing an engine for is Qt based then you may not need to override any of these methods (e.g. the tk-nuke engine).
- Parameters:
title – The title of the window. This will appear in the Toolkit title bar.
bundle – The app, engine or framework object that is associated with this window
widget_class (
PySide.QtGui.QWidget
) – The class of the UI to be constructed. This must derive from QWidget.
Additional parameters specified will be passed through to the widget_class constructor.
- Returns:
the created widget_class instance
- show_modal(title, bundle, widget_class, *args, **kwargs)[source]
Shows a modal dialog window in a way suitable for this engine. The engine will attempt to integrate it as seamlessly as possible into the host application. This call is blocking until the user closes the dialog. The dialog will be created with a standard Toolkit window title bar where the title will be displayed.
Note
In some cases, it is necessary to hide the standard Toolkit title bar. You can do this by adding a property to the widget class you are displaying:
@property def hide_tk_title_bar(self): "Tell the system to not show the standard toolkit toolbar" return True
- Parameters:
title – The title of the window
bundle – The app, engine or framework object that is associated with this window
widget_class (
PySide.QtGui.QWidget
) – The class of the UI to be constructed. This must derive from QWidget.
Additional parameters specified will be passed through to the widget_class constructor.
- Returns:
(a standard QT dialog status return code, the created widget_class instance)
- show_panel(panel_id, title, bundle, widget_class, *args, **kwargs)[source]
Shows a panel in a way suitable for this engine. Engines should attempt to integrate panel support as seamlessly as possible into the host application. Some engines have extensive panel support and workflows, others have none at all.
If the engine does not specifically implement panel support, the window will be shown as a modeless dialog instead and the call is equivalent to calling
show_dialog()
.The dialog will be created with a standard Toolkit window title bar where the title will be displayed.
Note
In some cases, it is necessary to hide the standard Toolkit title bar. You can do this by adding a property to the widget class you are displaying:
@property def hide_tk_title_bar(self): "Tell the system to not show the standard toolkit toolbar" return True
- Parameters:
panel_id – Unique identifier for the panel, as obtained by register_panel().
title – The title of the panel
bundle – The app, engine or framework object that is associated with this window
widget_class (
PySide.QtGui.QWidget
) – The class of the UI to be constructed. This must derive from QWidget.
Additional parameters specified will be passed through to the widget_class constructor.
- Returns:
the created widget_class instance
- property cache_location
An item-specific location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/PROJECT_ID/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions:
stored_query_data_path = os.path.join(self.cache_location, "query.dat")
- property context_change_allowed
Whether a context change is allowed without the need for a restart. If a bundle supports on-the-fly context changing, this property should be overridden in the deriving class and forced to return True.
- Returns:
bool
- create_hook_instance(hook_expression, base_class=None)
Returns the instance of a hook object given an expression.
This is useful for complex workflows where it is beneficial to maintain a handle to a hook instance. Normally, hooks are stateless and every time a hook is called, a new instance is returned. This method provides a standardized way to retrieve an instance of a hook:
self._plugin = app_object.create_hook_instance("{config}/path/to/my_hook.py") self._plugin.execute_method_x() self._plugin.execute_method_y() self._plugin.execute_method_z()
The hook expression is the raw value that is specified in the configuration file. If you want to access a configuration setting instead (like how for example
execute_hook_method()
works), simply callget_setting()
to retrieve the value and then pass the settings value to this method.Note
For more information about hook syntax, see
Hook
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that create hook instances at execution time and wish to provide default implementation without the need to configure the base hook. The supplied class must inherit from Hook.- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
Hook
instance.
- property description
A short description of the item
- Returns:
string
- property disk_location
The folder on disk where this item is located. This can be useful if you want to write app code to retrieve a local resource:
app_font = os.path.join(self.disk_location, "resources", "font.fnt")
- property display_name
The display name for the item (e.g. Maya Engine)
- Returns:
display name as string
- property documentation_url
Return the relevant documentation url for this item.
- Returns:
url string, None if no documentation was found
- ensure_folder_exists(path)
Make sure that the given folder exists on disk. Convenience method to make it easy for apps and engines to create folders in a standardized fashion. While the creation of high level folder structure such as Shot and Asset folders is typically handled by the folder creation system in Toolkit, Apps tend to need to create leaf-level folders such as publish folders and work areas. These are often created just in time of the operation.
Note
This method calls out to the
ensure_folder_exists
core hook, making the I/O operation user configurable. We recommend using this method over the methods provided insgtk.util.filesystem
.- Parameters:
path – path to create
- execute_hook(key, base_class=None, **kwargs)
Execute a hook that is part of the environment configuration for the current bundle.
Convenience method that calls
execute_hook_method()
with the method_name parameter set to “execute”.Warning
This method is present for backwards compatibility. For all new hooks, we recommend using
execute_hook_method()
instead.You simply pass the name of the hook setting that you want to execute and the accompanying arguments, and toolkit will find the correct hook file based on the currently configured setting and then execute the execute() method for that hook.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_by_name(hook_name, **kwargs)
Execute an arbitrary hook located in the hooks folder for this project. The hook_name is the name of the python file in which the hook resides, without the file extension.
In most use cases, the execute_hook method is the preferred way to access a hook from an app.
Warning
Now deprecated - Please use
execute_hook_expression()
instead.This method is typically only used when you want to execute an arbitrary list of hooks, for example if you want to run a series of arbitrary user defined pre-publish validation hooks.
Note
For more information about hooks, see
Hook
- Parameters:
hook_name – name of the legacy hook file to execute.
- execute_hook_expression(hook_expression, method_name, base_class=None, **kwargs)
Execute an arbitrary hook via an expression. While the methods execute_hook and execute_hook_method allows you to execute a particular hook setting as specified in the app configuration manifest, this methods allows you to execute a hook directly by passing a hook expression, for example
{config}/path/to/my_hook.py
This is useful if you are doing rapid app development and don’t necessarily want to expose a hook as a configuration setting just yet. It is also useful if you have app settings that are nested deep inside of lists or dictionaries. In that case, you cannot use execute_hook, but instead will have to retrieve the value specifically and then run it.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
method_name – Method inside the hook to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_method(key, method_name, base_class=None, **kwargs)
Execute a specific method in a hook that is part of the environment configuration for the current bundle.
You simply pass the name of the hook setting that you want to execute, the name of the method you want to execute and the accompanying arguments. Toolkit will find the correct hook file based on the currently configured setting and then execute the specified method.
Hooks form a flexible way to extend and make toolkit apps or engines configurable. A hook acts like a setting in that it needs to be configured as part of the app’s configuration, but instead of being a simple value, it is a code snippet contained inside a class.
Apps typically provide default hooks to make installation and overriding easy. Each hook is represented by a setting, similar to the ones you access via the
get_setting()
method, however instead of retrieving a fixed value, you execute code which generates a value.This method will execute a specific method for a given hook setting. Toolkit will find the actual python hook file and handle initialization and execution for you, by looking at the configuration settings and resolve a path based on this.
Arguments should always be passed in by name. This is to make it easy to add new parameters without breaking backwards compatibility, for example
execute_hook_method("validator", "pre_check", name=curr_scene, version=curr_ver).
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
method_name – Name of the method to execute
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- property frameworks
List of all frameworks associated with this item
- Returns:
List of framework objects
- get_project_cache_location(project_id)
Gets the bundle’s cache-location path for the given project id.
- Parameters:
project_id (int) – The project Entity id number.
- Returns:
Cache location directory path.
- Rtype str:
- get_setting(key, default=None)
Get a value from the item’s settings:
>>> app.get_setting('entity_types') ['Sequence', 'Shot', 'Asset', 'Task']
- Parameters:
key – config name
default – default value to return
- Returns:
Value from the environment configuration
- get_template(key)
Returns a template object for a particular template setting in the Framework configuration. This method will look at the app configuration, determine which template is being referred to in the setting, go into the main platform Template API and fetch that particular template object.
This is a convenience method. Shorthand for
self.sgtk.templates[ self.get_setting(key) ]
.- Parameters:
key – Setting to retrieve template for
- Returns:
Template
object
- get_template_by_name(template_name)
Note: This is for advanced use cases - Most of the time you should probably use
get_template()
. Find a particular template, the way it is named in the master config filetemplates.yml
. This method will access the master templates file directly and pull out a specifically named template without using the app config. Note that using this method may result in code which is less portable across studios, since it makes assumptions about how templates are named and defined in the master config. Generally speaking, it is often better to access templates using the app configuration and the get_template() method.This is a convenience method. Shorthand for
self.sgtk.templates[template_name]
.:param template_name :returns:
Template
object
- property icon_256
Path to a 256x256 pixel png file which describes the item
- import_module(module_name)
Special import command for Toolkit bundles. Imports the python folder inside an app and returns the specified module name that exists inside the python folder.
Each Toolkit App or Engine can have a python folder which contains additional code. In order to ensure that Toolkit can run multiple versions of the same app, as well as being able to reload app code if it changes, it is recommended that this method is used whenever you want to access code in the python location.
For example, imagine you had the following structure:
tk-multi-mybundle |- app.py or engine.py or framework.py |- info.yml \- python |- __init__.py <--- Needs to contain 'from . import tk_multi_mybundle' \- tk_multi_mybundle
The above structure is a standard Toolkit app outline.
app.py
is a light weight wrapper and the python moduletk_multi_myapp
module contains the actual code payload. In order to import this in a Toolkit friendly way, you need to run the following when you want to load the payload module inside of app.py:module_obj = self.import_module("tk_multi_myapp")
- property logger
Standard python
Logger
for this engine, app or framework.Use this whenever you want to emit or process log messages. If you are developing an app, engine or framework, call this method for generic logging.
Note
Inside the
python
area of your app, engine or framework, we recommend that you usesgtk.platform.get_logger()
for your logging.Logging will be dispatched to a logger parented under the main toolkit logging namespace:
# pattern sgtk.env.environment_name.engine_instance_name # for example sgtk.env.asset.tk-maya
Note
If you want all log messages that you are emitting in your app, engine or framework to be written to a log file or to a logging console, you can attach a std log handler here.
- property name
The short name for the item (e.g. tk-maya)
- Returns:
name as string
- post_context_change(old_context, new_context)
Called after a context change.
Implemented by deriving classes.
- pre_context_change(old_context, new_context)
Called before a context change.
Implemented by deriving classes.
- property sgtk
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property site_cache_location
A site location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions and can be shared across a site:
stored_query_data_path = os.path.join(self.site_cache_location, "query.dat")
- property style_constants
Returns a dictionary of style constants. These can be used to build UIs using standard colors and other style components. All keys returned in this dictionary can also be used inside a style.qss that lives at the root level of the app, engine or framework. Use a
{{DOUBLE_BACKET}}
syntax in the stylesheet file, for example:QWidget { color: {{SG_FOREGROUND_COLOR}}; }
This property returns the values for all constants, for example:
{ "SG_HIGHLIGHT_COLOR": "#18A7E3", "SG_ALERT_COLOR": "#FC6246", "SG_FOREGROUND_COLOR": "#C8C8C8" }
- Returns:
Dictionary. See above for example
- property support_url
Return the relevant support url for this item.
- Returns:
url string, None if no documentation was found
- property tank
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property version
The version of the item (e.g. ‘v0.2.3’)
- Returns:
string representing the version
Applications
Toolkit Apps are tools that can execute in multiple DCC environments.
Note
For an introduction to App development, see https://help.autodesk.com/view/SGDEV/ENU/?contextId=PG_SGTK_DEVELOPER_APP
A good way to get started with App development is to clone our starter github repository https://github.com/shotgunsoftware/tk-multi-starterapp. Please note that there are different branches demonstrating different types of apps with a varying degree of complexity.
Application
- class sgtk.platform.Application(engine, descriptor, settings, instance_name, env)[source]
Base class for all Applications (Apps) running in Toolkit.
Application instances are constructed by the toolkit launch process and various factory methods such as
start_engine()
.- Parameters:
engine – The engine instance to connect this app to
app_name – The short name of this app (e.g. tk-nukepublish)
settings – a settings dictionary for this app
- property shotgun
Returns a Shotgun API handle associated with the currently running environment. This method is a convenience method that calls out to
shotgun()
.- Returns:
Shotgun API handle
- property instance_name
The name for this app instance.
- property engine
The engine that this app is connected to.
- get_metrics_properties()[source]
Returns a dictionary with properties to use when emitting a metric event for this application in the current engine.
The dictionary contains information about this application, about the current engine, and about the application hosting the engine. For each of them, a name and a version string are available:
{ 'Host App': 'Maya', 'Host App Version': '2017', 'Engine': 'tk-maya', 'Engine Version': 'v0.4.1', 'App': 'tk-multi-about', 'App Version': '1.2.3' }
- Returns:
Dictionary with info per above.
- init_app()[source]
Implemented by deriving classes in order to initialize the app Called by the engine as it loads the app.
- post_engine_init()[source]
Implemented by deriving classes in order to run code after the engine has completely finished initializing itself and all its apps. At this point, the engine has a fully populated apps dictionary and all loaded apps have been fully initialized and validated.
- destroy_app()[source]
Implemented by deriving classes in order to tear down the app Called by the engine as it is being destroyed.
- event_engine(event)[source]
Called when the parent engine emits an event. This method is intended to be overridden by deriving classes in order to implement event-specific behavior.
Note
This method is called for all engine event types. If overriding this method to implement an event handler in a specific app, the event object received will need to be checked via isinstance (or via its event_type property) to know what event has been triggered. As there are also type specific event handlers available, it is considered best practice to use those in all cases except those where a generic handler is absolutely required.
Warning
It is possible that events will be triggered quite frequently. It is important to keep performance in mind when writing an event handler.
- Parameters:
event (
EngineEvent
) – The event object that was emitted.
- event_file_open(event)[source]
Called when the parent engine emits a file-open event. This method is intended to be overridden by deriving classes.
Warning
It is possible that events will be triggered quite frequently. It is important to keep performance in mind when writing an event handler.
- Parameters:
event (
FileOpenEvent
) – The event object that was emitted.
- event_file_close(event)[source]
Called when the parent engine emits a file-close event. This method is intended to be overridden by deriving classes.
Warning
It is possible that events will be triggered quite frequently. It is important to keep performance in mind when writing an event handler.
- Parameters:
event (
FileCloseEvent
) – The event object that was emitted.
- log_debug(msg)[source]
Logs a debug message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_info(msg)[source]
Logs an info message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_warning(msg)[source]
Logs an warning message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_error(msg)[source]
Logs an error message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_exception(msg)[source]
Logs an exception message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- property cache_location
An item-specific location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/PROJECT_ID/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions:
stored_query_data_path = os.path.join(self.cache_location, "query.dat")
- change_context(new_context)
Abstract method for context changing.
Implemented by deriving classes that wish to support context changes and require specific logic to do so safely.
- Parameters:
new_context – The context being changed to.
- property context_change_allowed
Whether a context change is allowed without the need for a restart. If a bundle supports on-the-fly context changing, this property should be overridden in the deriving class and forced to return True.
- Returns:
bool
- create_hook_instance(hook_expression, base_class=None)
Returns the instance of a hook object given an expression.
This is useful for complex workflows where it is beneficial to maintain a handle to a hook instance. Normally, hooks are stateless and every time a hook is called, a new instance is returned. This method provides a standardized way to retrieve an instance of a hook:
self._plugin = app_object.create_hook_instance("{config}/path/to/my_hook.py") self._plugin.execute_method_x() self._plugin.execute_method_y() self._plugin.execute_method_z()
The hook expression is the raw value that is specified in the configuration file. If you want to access a configuration setting instead (like how for example
execute_hook_method()
works), simply callget_setting()
to retrieve the value and then pass the settings value to this method.Note
For more information about hook syntax, see
Hook
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that create hook instances at execution time and wish to provide default implementation without the need to configure the base hook. The supplied class must inherit from Hook.- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
Hook
instance.
- property description
A short description of the item
- Returns:
string
- property disk_location
The folder on disk where this item is located. This can be useful if you want to write app code to retrieve a local resource:
app_font = os.path.join(self.disk_location, "resources", "font.fnt")
- property display_name
The display name for the item (e.g. Maya Engine)
- Returns:
display name as string
- property documentation_url
Return the relevant documentation url for this item.
- Returns:
url string, None if no documentation was found
- ensure_folder_exists(path)
Make sure that the given folder exists on disk. Convenience method to make it easy for apps and engines to create folders in a standardized fashion. While the creation of high level folder structure such as Shot and Asset folders is typically handled by the folder creation system in Toolkit, Apps tend to need to create leaf-level folders such as publish folders and work areas. These are often created just in time of the operation.
Note
This method calls out to the
ensure_folder_exists
core hook, making the I/O operation user configurable. We recommend using this method over the methods provided insgtk.util.filesystem
.- Parameters:
path – path to create
- execute_hook(key, base_class=None, **kwargs)
Execute a hook that is part of the environment configuration for the current bundle.
Convenience method that calls
execute_hook_method()
with the method_name parameter set to “execute”.Warning
This method is present for backwards compatibility. For all new hooks, we recommend using
execute_hook_method()
instead.You simply pass the name of the hook setting that you want to execute and the accompanying arguments, and toolkit will find the correct hook file based on the currently configured setting and then execute the execute() method for that hook.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_by_name(hook_name, **kwargs)
Execute an arbitrary hook located in the hooks folder for this project. The hook_name is the name of the python file in which the hook resides, without the file extension.
In most use cases, the execute_hook method is the preferred way to access a hook from an app.
Warning
Now deprecated - Please use
execute_hook_expression()
instead.This method is typically only used when you want to execute an arbitrary list of hooks, for example if you want to run a series of arbitrary user defined pre-publish validation hooks.
Note
For more information about hooks, see
Hook
- Parameters:
hook_name – name of the legacy hook file to execute.
- execute_hook_expression(hook_expression, method_name, base_class=None, **kwargs)
Execute an arbitrary hook via an expression. While the methods execute_hook and execute_hook_method allows you to execute a particular hook setting as specified in the app configuration manifest, this methods allows you to execute a hook directly by passing a hook expression, for example
{config}/path/to/my_hook.py
This is useful if you are doing rapid app development and don’t necessarily want to expose a hook as a configuration setting just yet. It is also useful if you have app settings that are nested deep inside of lists or dictionaries. In that case, you cannot use execute_hook, but instead will have to retrieve the value specifically and then run it.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
method_name – Method inside the hook to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_method(key, method_name, base_class=None, **kwargs)
Execute a specific method in a hook that is part of the environment configuration for the current bundle.
You simply pass the name of the hook setting that you want to execute, the name of the method you want to execute and the accompanying arguments. Toolkit will find the correct hook file based on the currently configured setting and then execute the specified method.
Hooks form a flexible way to extend and make toolkit apps or engines configurable. A hook acts like a setting in that it needs to be configured as part of the app’s configuration, but instead of being a simple value, it is a code snippet contained inside a class.
Apps typically provide default hooks to make installation and overriding easy. Each hook is represented by a setting, similar to the ones you access via the
get_setting()
method, however instead of retrieving a fixed value, you execute code which generates a value.This method will execute a specific method for a given hook setting. Toolkit will find the actual python hook file and handle initialization and execution for you, by looking at the configuration settings and resolve a path based on this.
Arguments should always be passed in by name. This is to make it easy to add new parameters without breaking backwards compatibility, for example
execute_hook_method("validator", "pre_check", name=curr_scene, version=curr_ver).
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
method_name – Name of the method to execute
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- property frameworks
List of all frameworks associated with this item
- Returns:
List of framework objects
- get_project_cache_location(project_id)
Gets the bundle’s cache-location path for the given project id.
- Parameters:
project_id (int) – The project Entity id number.
- Returns:
Cache location directory path.
- Rtype str:
- get_setting(key, default=None)
Get a value from the item’s settings:
>>> app.get_setting('entity_types') ['Sequence', 'Shot', 'Asset', 'Task']
- Parameters:
key – config name
default – default value to return
- Returns:
Value from the environment configuration
- get_template(key)
Returns a template object for a particular template setting in the Framework configuration. This method will look at the app configuration, determine which template is being referred to in the setting, go into the main platform Template API and fetch that particular template object.
This is a convenience method. Shorthand for
self.sgtk.templates[ self.get_setting(key) ]
.- Parameters:
key – Setting to retrieve template for
- Returns:
Template
object
- get_template_by_name(template_name)
Note: This is for advanced use cases - Most of the time you should probably use
get_template()
. Find a particular template, the way it is named in the master config filetemplates.yml
. This method will access the master templates file directly and pull out a specifically named template without using the app config. Note that using this method may result in code which is less portable across studios, since it makes assumptions about how templates are named and defined in the master config. Generally speaking, it is often better to access templates using the app configuration and the get_template() method.This is a convenience method. Shorthand for
self.sgtk.templates[template_name]
.:param template_name :returns:
Template
object
- property icon_256
Path to a 256x256 pixel png file which describes the item
- import_module(module_name)
Special import command for Toolkit bundles. Imports the python folder inside an app and returns the specified module name that exists inside the python folder.
Each Toolkit App or Engine can have a python folder which contains additional code. In order to ensure that Toolkit can run multiple versions of the same app, as well as being able to reload app code if it changes, it is recommended that this method is used whenever you want to access code in the python location.
For example, imagine you had the following structure:
tk-multi-mybundle |- app.py or engine.py or framework.py |- info.yml \- python |- __init__.py <--- Needs to contain 'from . import tk_multi_mybundle' \- tk_multi_mybundle
The above structure is a standard Toolkit app outline.
app.py
is a light weight wrapper and the python moduletk_multi_myapp
module contains the actual code payload. In order to import this in a Toolkit friendly way, you need to run the following when you want to load the payload module inside of app.py:module_obj = self.import_module("tk_multi_myapp")
- property logger
Standard python
Logger
for this engine, app or framework.Use this whenever you want to emit or process log messages. If you are developing an app, engine or framework, call this method for generic logging.
Note
Inside the
python
area of your app, engine or framework, we recommend that you usesgtk.platform.get_logger()
for your logging.Logging will be dispatched to a logger parented under the main toolkit logging namespace:
# pattern sgtk.env.environment_name.engine_instance_name # for example sgtk.env.asset.tk-maya
Note
If you want all log messages that you are emitting in your app, engine or framework to be written to a log file or to a logging console, you can attach a std log handler here.
- property name
The short name for the item (e.g. tk-maya)
- Returns:
name as string
- post_context_change(old_context, new_context)
Called after a context change.
Implemented by deriving classes.
- pre_context_change(old_context, new_context)
Called before a context change.
Implemented by deriving classes.
- property sgtk
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property site_cache_location
A site location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions and can be shared across a site:
stored_query_data_path = os.path.join(self.site_cache_location, "query.dat")
- property style_constants
Returns a dictionary of style constants. These can be used to build UIs using standard colors and other style components. All keys returned in this dictionary can also be used inside a style.qss that lives at the root level of the app, engine or framework. Use a
{{DOUBLE_BACKET}}
syntax in the stylesheet file, for example:QWidget { color: {{SG_FOREGROUND_COLOR}}; }
This property returns the values for all constants, for example:
{ "SG_HIGHLIGHT_COLOR": "#18A7E3", "SG_ALERT_COLOR": "#FC6246", "SG_FOREGROUND_COLOR": "#C8C8C8" }
- Returns:
Dictionary. See above for example
- property support_url
Return the relevant support url for this item.
- Returns:
url string, None if no documentation was found
- property tank
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property version
The version of the item (e.g. ‘v0.2.3’)
- Returns:
string representing the version
Frameworks
Frameworks are like libraries. They contain functionality that can be shared and reused across apps or engines.
Frameworks are automatically imported into the system whenever Toolkit finds a framework defined in the info.yml for an app or an engine. Once imported, it will be available in the frameworks dictionary on the host object. For example, an app or engine (or framework) may have the following definition in its info.yml:
frameworks:
- {"name": "tk-framework-widget", "version": "v0.1.2"}
- {"name": "tk-framework-tools", "version": "v0.x.x"}
When Toolkit loads the app, it will verify that the two frameworks are present in the environment and
initialize them. Once initialized, the app that needs them can access them via the self.frameworks
property:
foo_bar_module = self.frameworks["tk-framework-widget"].import_module("foo_bar")
In order to import a framework module into app or engine code, use the convenience method
import_framework()
. This method is typically executed right in the beginning
of the file, before you create any methods or classes:
import os
import sys
import sgtk
widgets = sgtk.platform.import_framework("tk-framework-widget", "widgets")
class MyBrowser(widgets.BrowserWidget):
...
If you would like to load the framework instance itself rather than a module which was imported as part of
the framework initalization, you can use the get_framework()
method:
import sgtk
fw = sgtk.platform.get_framework("tk-framework-widget")
Note that this only works inside of code which has been imported via the import_module command - e.g. the way we recommend that Sgtk code is being imported into apps. For other scenarios, use the frameworks dictionary in conjunction with the import_module command, as shown above.
Frameworks are imported in an individual fashion, meaning that even though a framework is used in two apps, each app will import its own instance of the framework. This is to ensure stability and encapsulation.
A framework works just like an app or an engine - it has an info.yml manifest, a framework.py file which typically contains a class which derives from the Framework base class, etc.
Framework
- class sgtk.platform.Framework(engine, descriptor, settings, env)[source]
Base class for a Toolkit Framework
Called by the bundle loading framework. The constructor is not meant to be overridden by deriving classes.
- Parameters:
engine (
Engine
) – The engine instance to connect this fw toapp_name – The short name of this framework (e.g. tk-framework-widget)
settings – a settings dictionary for this fw
env – the environment that the framework belongs to
- property shotgun
Returns a Shotgun API handle associated with the currently running environment. This method is a convenience method that calls out to
shotgun()
.- Returns:
Shotgun API handle
- property engine
The engine that this framework is connected to
Boolean indicating whether this is a shared framework.
Frameworks are shared by default and this is a setting that can be controlled by the bundle manifest.
When a framework is shared, a single copy of the code is shared across all apps that use it. All apps will cut their framework instances from the same code. Any global state within the framework will be shared across all framework instances, and hence across all different apps.
If your framework manages complex global state that you want to control precisely, it may be useful to set the framework to be not shared in the
info.yml
manifest file. This will ensure that each bundle that uses the framework will maintain it’s own private version of the framework code.
- get_metrics_properties()[source]
Returns a dictionary with properties to use when emitting a metric event for this framework in the current engine.
Frameworks don’t have any particular properties and just return the result of
Engine.get_metrics_properties()
.- Returns:
Dictionary as per above.
- init_framework()[source]
Implemented by deriving classes in order to initialize the app. Called by the engine as it loads the framework.
- destroy_framework()[source]
Implemented by deriving classes in order to tear down the framework. Called by the engine as it is being destroyed.
- log_debug(msg)[source]
Logs a debug message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_info(msg)[source]
Logs an info message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_warning(msg)[source]
Logs an warning message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_error(msg)[source]
Logs an error message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- log_exception(msg)[source]
Logs an exception message.
Deprecated since version 0.18: Use
Engine.logger()
instead.- Parameters:
msg – Message to log.
- property cache_location
An item-specific location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/PROJECT_ID/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions:
stored_query_data_path = os.path.join(self.cache_location, "query.dat")
- change_context(new_context)
Abstract method for context changing.
Implemented by deriving classes that wish to support context changes and require specific logic to do so safely.
- Parameters:
new_context – The context being changed to.
- property context_change_allowed
Whether a context change is allowed without the need for a restart. If a bundle supports on-the-fly context changing, this property should be overridden in the deriving class and forced to return True.
- Returns:
bool
- create_hook_instance(hook_expression, base_class=None)
Returns the instance of a hook object given an expression.
This is useful for complex workflows where it is beneficial to maintain a handle to a hook instance. Normally, hooks are stateless and every time a hook is called, a new instance is returned. This method provides a standardized way to retrieve an instance of a hook:
self._plugin = app_object.create_hook_instance("{config}/path/to/my_hook.py") self._plugin.execute_method_x() self._plugin.execute_method_y() self._plugin.execute_method_z()
The hook expression is the raw value that is specified in the configuration file. If you want to access a configuration setting instead (like how for example
execute_hook_method()
works), simply callget_setting()
to retrieve the value and then pass the settings value to this method.Note
For more information about hook syntax, see
Hook
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that create hook instances at execution time and wish to provide default implementation without the need to configure the base hook. The supplied class must inherit from Hook.- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
Hook
instance.
- property description
A short description of the item
- Returns:
string
- property disk_location
The folder on disk where this item is located. This can be useful if you want to write app code to retrieve a local resource:
app_font = os.path.join(self.disk_location, "resources", "font.fnt")
- property display_name
The display name for the item (e.g. Maya Engine)
- Returns:
display name as string
- property documentation_url
Return the relevant documentation url for this item.
- Returns:
url string, None if no documentation was found
- ensure_folder_exists(path)
Make sure that the given folder exists on disk. Convenience method to make it easy for apps and engines to create folders in a standardized fashion. While the creation of high level folder structure such as Shot and Asset folders is typically handled by the folder creation system in Toolkit, Apps tend to need to create leaf-level folders such as publish folders and work areas. These are often created just in time of the operation.
Note
This method calls out to the
ensure_folder_exists
core hook, making the I/O operation user configurable. We recommend using this method over the methods provided insgtk.util.filesystem
.- Parameters:
path – path to create
- execute_hook(key, base_class=None, **kwargs)
Execute a hook that is part of the environment configuration for the current bundle.
Convenience method that calls
execute_hook_method()
with the method_name parameter set to “execute”.Warning
This method is present for backwards compatibility. For all new hooks, we recommend using
execute_hook_method()
instead.You simply pass the name of the hook setting that you want to execute and the accompanying arguments, and toolkit will find the correct hook file based on the currently configured setting and then execute the execute() method for that hook.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_by_name(hook_name, **kwargs)
Execute an arbitrary hook located in the hooks folder for this project. The hook_name is the name of the python file in which the hook resides, without the file extension.
In most use cases, the execute_hook method is the preferred way to access a hook from an app.
Warning
Now deprecated - Please use
execute_hook_expression()
instead.This method is typically only used when you want to execute an arbitrary list of hooks, for example if you want to run a series of arbitrary user defined pre-publish validation hooks.
Note
For more information about hooks, see
Hook
- Parameters:
hook_name – name of the legacy hook file to execute.
- execute_hook_expression(hook_expression, method_name, base_class=None, **kwargs)
Execute an arbitrary hook via an expression. While the methods execute_hook and execute_hook_method allows you to execute a particular hook setting as specified in the app configuration manifest, this methods allows you to execute a hook directly by passing a hook expression, for example
{config}/path/to/my_hook.py
This is useful if you are doing rapid app development and don’t necessarily want to expose a hook as a configuration setting just yet. It is also useful if you have app settings that are nested deep inside of lists or dictionaries. In that case, you cannot use execute_hook, but instead will have to retrieve the value specifically and then run it.
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
hook_expression – Path to hook to execute. See above for syntax details.
method_name – Method inside the hook to execute.
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- execute_hook_method(key, method_name, base_class=None, **kwargs)
Execute a specific method in a hook that is part of the environment configuration for the current bundle.
You simply pass the name of the hook setting that you want to execute, the name of the method you want to execute and the accompanying arguments. Toolkit will find the correct hook file based on the currently configured setting and then execute the specified method.
Hooks form a flexible way to extend and make toolkit apps or engines configurable. A hook acts like a setting in that it needs to be configured as part of the app’s configuration, but instead of being a simple value, it is a code snippet contained inside a class.
Apps typically provide default hooks to make installation and overriding easy. Each hook is represented by a setting, similar to the ones you access via the
get_setting()
method, however instead of retrieving a fixed value, you execute code which generates a value.This method will execute a specific method for a given hook setting. Toolkit will find the actual python hook file and handle initialization and execution for you, by looking at the configuration settings and resolve a path based on this.
Arguments should always be passed in by name. This is to make it easy to add new parameters without breaking backwards compatibility, for example
execute_hook_method("validator", "pre_check", name=curr_scene, version=curr_ver).
An optional
base_class
can be provided to override the defaultHook
base class. This is useful for bundles that want to define and document a strict interface for hooks. The classes defined in the hook have to derived from this classes.Note
For more information about hooks, see
Hook
- Parameters:
key – The name of the hook setting you want to execute.
method_name – Name of the method to execute
base_class – A python class to use as the base class for the created hook. This will override the default hook base class,
Hook
.
- Returns:
The return value from the hook
- property frameworks
List of all frameworks associated with this item
- Returns:
List of framework objects
- get_project_cache_location(project_id)
Gets the bundle’s cache-location path for the given project id.
- Parameters:
project_id (int) – The project Entity id number.
- Returns:
Cache location directory path.
- Rtype str:
- get_setting(key, default=None)
Get a value from the item’s settings:
>>> app.get_setting('entity_types') ['Sequence', 'Shot', 'Asset', 'Task']
- Parameters:
key – config name
default – default value to return
- Returns:
Value from the environment configuration
- get_template(key)
Returns a template object for a particular template setting in the Framework configuration. This method will look at the app configuration, determine which template is being referred to in the setting, go into the main platform Template API and fetch that particular template object.
This is a convenience method. Shorthand for
self.sgtk.templates[ self.get_setting(key) ]
.- Parameters:
key – Setting to retrieve template for
- Returns:
Template
object
- get_template_by_name(template_name)
Note: This is for advanced use cases - Most of the time you should probably use
get_template()
. Find a particular template, the way it is named in the master config filetemplates.yml
. This method will access the master templates file directly and pull out a specifically named template without using the app config. Note that using this method may result in code which is less portable across studios, since it makes assumptions about how templates are named and defined in the master config. Generally speaking, it is often better to access templates using the app configuration and the get_template() method.This is a convenience method. Shorthand for
self.sgtk.templates[template_name]
.:param template_name :returns:
Template
object
- property icon_256
Path to a 256x256 pixel png file which describes the item
- import_module(module_name)
Special import command for Toolkit bundles. Imports the python folder inside an app and returns the specified module name that exists inside the python folder.
Each Toolkit App or Engine can have a python folder which contains additional code. In order to ensure that Toolkit can run multiple versions of the same app, as well as being able to reload app code if it changes, it is recommended that this method is used whenever you want to access code in the python location.
For example, imagine you had the following structure:
tk-multi-mybundle |- app.py or engine.py or framework.py |- info.yml \- python |- __init__.py <--- Needs to contain 'from . import tk_multi_mybundle' \- tk_multi_mybundle
The above structure is a standard Toolkit app outline.
app.py
is a light weight wrapper and the python moduletk_multi_myapp
module contains the actual code payload. In order to import this in a Toolkit friendly way, you need to run the following when you want to load the payload module inside of app.py:module_obj = self.import_module("tk_multi_myapp")
- property logger
Standard python
Logger
for this engine, app or framework.Use this whenever you want to emit or process log messages. If you are developing an app, engine or framework, call this method for generic logging.
Note
Inside the
python
area of your app, engine or framework, we recommend that you usesgtk.platform.get_logger()
for your logging.Logging will be dispatched to a logger parented under the main toolkit logging namespace:
# pattern sgtk.env.environment_name.engine_instance_name # for example sgtk.env.asset.tk-maya
Note
If you want all log messages that you are emitting in your app, engine or framework to be written to a log file or to a logging console, you can attach a std log handler here.
- property name
The short name for the item (e.g. tk-maya)
- Returns:
name as string
- post_context_change(old_context, new_context)
Called after a context change.
Implemented by deriving classes.
- pre_context_change(old_context, new_context)
Called before a context change.
Implemented by deriving classes.
- property sgtk
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property site_cache_location
A site location on disk where the app or engine can store random cache data. This location is guaranteed to exist on disk.
This location is configurable via the
cache_location
hook. It typically points at a path in the local filesystem, e.g on a mac:~/Library/Caches/Shotgun/SITENAME/BUNDLE_NAME
This can be used to store cache data that the app wants to reuse across sessions and can be shared across a site:
stored_query_data_path = os.path.join(self.site_cache_location, "query.dat")
- property style_constants
Returns a dictionary of style constants. These can be used to build UIs using standard colors and other style components. All keys returned in this dictionary can also be used inside a style.qss that lives at the root level of the app, engine or framework. Use a
{{DOUBLE_BACKET}}
syntax in the stylesheet file, for example:QWidget { color: {{SG_FOREGROUND_COLOR}}; }
This property returns the values for all constants, for example:
{ "SG_HIGHLIGHT_COLOR": "#18A7E3", "SG_ALERT_COLOR": "#FC6246", "SG_FOREGROUND_COLOR": "#C8C8C8" }
- Returns:
Dictionary. See above for example
- property support_url
Return the relevant support url for this item.
- Returns:
url string, None if no documentation was found
- property tank
Returns the Toolkit API instance associated with this item
- Returns:
Tank
- property version
The version of the item (e.g. ‘v0.2.3’)
- Returns:
string representing the version
Exceptions
The following exception types are raised from within sgtk.platform
:
- class sgtk.platform.TankEngineInitError[source]
Bases:
TankError
Exception that indicates that an engine could not start up.
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- class sgtk.platform.TankMissingEngineError[source]
Bases:
TankEngineInitError
Exception that indicates that an engine could not start up.
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- class sgtk.platform.TankContextChangeNotSupportedError[source]
Bases:
TankError
Exception that indicates that a requested context change is not allowed based on a check of the current engine and all of its active apps.
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
App and Engine Development
Import and access methods
The following methods are useful when writing app, engine or framework code and you need to access and load other objects.
Note
For examples of how the below methods are used in practice, check out our starter app https://github.com/shotgunsoftware/tk-multi-starterapp.
- sgtk.platform.current_bundle(*args, **kwargs)
Returns the bundle (app, engine or framework) instance for the app that the calling code is associated with. This is a special method, designed to be used inside python modules that belong to apps, engines or frameworks.
The calling code needs to have been imported using toolkit’s standard import mechanism,
Application.import_module()
, otherwise an exception will be raised.This special helper method can be useful when code deep inside an app needs to reach out to for example grab a configuration value. Then you can simply do:
app = sgtk.platform.current_bundle() app.get_setting("frame_range")
- Returns:
Application
,Engine
orFramework
instance
- sgtk.platform.get_framework(framework)[source]
Convenience method that returns a framework instance given a framework name.
This is a special method, designed to be used inside python modules that belong to apps, engines or frameworks.
The calling code needs to have been imported using toolkit’s standard import mechanism, import_module(), otherwise an exception will be raised.
For example, if your app code requires the tk-framework-helpers framework, and you need to retrieve a configuration setting from this framework, then you can simply do:
fw = sgtk.platform.get_framework("tk-framework-helpers") fw.get_setting("frame_range")
- Parameters:
framework – name of the framework object to access, as defined in the app’s info.yml manifest.
- Returns:
framework instance
- Type:
- sgtk.platform.import_framework(*args, **kwargs)
Convenience method for using frameworks code inside of apps, engines and other frameworks.
This method is intended to replace an import statement. Instead of typing:
from . import foo_bar
You use the following syntax to load a framework module:
foo_bar = tank.platform.import_framework("tk-framework-mystuff", "foo_bar")
This is a special method, designed to be used inside python modules that belong to apps, engines or frameworks.
The calling code needs to have been imported using toolkit’s standard import mechanism,
Bundle.import_module()
, otherwise an exception will be raised.- Parameters:
framework – name of the framework object to access, as defined in the app’s info.yml manifest.
module – module to load from framework
- sgtk.platform.get_logger(x)
Manifest file
Apps, engines and frameworks each have a metadata file. It always resides in the root of the app or engine
and is always named info.yml
. This metadata file contains important information about the item:
Configuration settings available for an app or engine.
Display name, documentation and support links for the app and other user facing metadata.
All custom Flow Production Tracking fields that are required by the app or engine code.
Which frameworks that are required in order for this app to run. You can specify an exact framework version to use (e.g.
v1.2.3
) or you can track against a subset of versions (e.g.v1.x.x
orv1.2.x
).
For real-world examples, see for example the Flow Production Tracking Loader or the Flow Production Tracking Nuke Write Node.
Display name and Description
The optional display_name
field defines the name that the user will see for the app.
The optional description
field is a brief one line description of what the app does:
# More verbose description of this item
display_name: "Write Node"
description: "Support for the Flow Production Tracking Write Node in Nuke."
Frameworks
If a framework is being used by the app, declare it in the info.yml
and Toolkit will keep track
and make sure that it is installed and available. Once inside the app, use the call
module_name = sgtk.platform.import_framework("tk-framework-name", "module_name")
. For more
information, see import_framework()
.
The frameworks section is a list of dictionaries, for example:
# the frameworks required to run this app
frameworks:
- {"name": "tk-framework-shotgunutils", "version": "v2.x.x"}
- {"name": "tk-framework-qtwidgets", "version": "v1.x.x"}
Version numbers are typically supplied on the form v1.x.x
, meaning that it will try to use
the most recent approved major version one of the framework. Toolkit uses semantic versioning
(http://semver.org/) for its versioning, meaning that major version numbers indicate breaking changes,
minor version number increments indicates added features and patch version numbers indicates backwards
compatible bug fixes. We therefore recommend to have framework dependencies track against a
major version number.
Note
If there is a required minimum version for a framework, a minimum_version
setting can be used in the info.yml
manifest:
# the frameworks required to run this app
frameworks:
- {"name": "tk-framework-shotgunutils", "version": "v2.x.x", "minimum_version": "v2.3.3"}
- {"name": "tk-framework-qtwidgets", "version": "v1.x.x"}
Version constraints
If your app or engine requires specific versions of shotgun, the core or other things,
you can specify this using the three parameters requires_shotgun_version
, requires_core_version
and (for apps only) requires_engine_version
:
# Required minimum versions for this item to run
requires_shotgun_version:
requires_core_version: "v0.14.37"
requires_engine_version: "v0.2.3"
Supported Engines and operating systems
If your app is not a multi-app that has been designed to run in any engine, use this option to specify which engines are supported:
supported_engines: [tk-nuke]
If your app or engine only supports a particular operating system, you can define this
using a supported_platforms
parameter. Valid values are linux
, windows
and mac
.
This is an optional setting - omitting it or leaving it blank means that the app
or engine will work on all platforms.
Documentation and Support
If you are developing an app for the Toolkit app store, no need to fill this in - the Sgtk Store
manages documentation separately! But if you are building an in-house app for your studio,
it can be useful to link it up to a wiki page or similar, so that it is easy for users to
jump straight from the app to the documentation. In this case, just add a documentation_url
setting:
documentation_url: "http://intranet/path/to/sgtk_app_docs.html"
This will be picked up and displayed in various locations in the Toolkit UI.
Similar to the documentation url above, it can be useful to connect an app to a url
which lets users know how to get help when they have questions. If you have a separate
system in your studio, (like an Request Tracker setup), and you want to make it easy for
users to reach out from the app to this system, just define a support_url
url:
support_url: "http://intranet/pipeline_team/support.html"
This setting is optional, and if left blank, the support url will automatically be the standard Toolkit support location.
Required Context Fields
Toolkit has a Context
which it uses to determine what the current Shot, Project or Asset is.
Apps may require a particular feature to be present in the context - for example, a
Loader App may require an entity to be present in order to show a list of items for the
current Asset or Shot. The required_context
settings help defining what fields are needed.
Possible values are project
, entity
, step
, task
and user
.
Flow Production Tracking fields
If your app requires a particular custom field in Flow Production Tracking to operate correctly, you can add this to the
info.yml
manifest. Whenever the app is installed into a setup, toolkit will ensure that this custom field exists in shotgun.
Just create a requires_shotgun_fields
in your manifest and add entries on the following form:
requires_shotgun_fields:
# Flow Production Tracking fields that this app expects
Version:
- { "system_name": "sg_movie_type", "type": "text" }
Note how the fields are grouped by Flow Production Tracking entity type (in the example above Version
). For a list of
which field types are supported,
see the Flow Production Tracking API documentation.
Note
For more complex field types (such as entity and multi entity links), you need to set up creation via the post-install hook instead. (The post install hook is a special hook which runs at installation time and allows you to execute arbitrary code as part of the app installation process.)
Warning
This functionality requires administrative privileges in Flow Production Tracking. Apps using this functionality therefore requires a workflow where an admin is required to update or install such apps. For general purpose apps, we therefore recommend not relying on this functionality.
The configuration section
If an app requires a setting in its code, it should be defined in the info.yml. Toolkit will validate all requested settings and make sure that they have all been defined (or have default values) and are valid before apps are launched. It will also use this metadata when performing upgrades and installations.
A single configuration entry typically looks like this:
setting_name:
type: some_type
description: "description of the setting"
default_value: general_default
default_value_tk-nuke: nuke_default
default_value_tk-maya: maya_default
option1: option_value1
option2: option_value2
Data types typically have specific optional settings. All data types and their options are outlines in the following sections.
A full configuration section inside an info.yml
manifest may look like this:
configuration:
shotgun_fields_hook:
type: hook
default_value: "{self}/shotgun_fields.py"
description: Hook which controls how values are presented
action_mappings:
type: dict
description: Associates shotgun objects with actions. The actions are all defined
inside the actions hook, so a number of actions are available by default
and in addition to this you can add your own actions if you like.
default_value:
Task:
- { actions: [assign_task, task_to_ip], filters: {} }
Version:
- { actions: [quicktime_clipboard, sequence_clipboard], filters: {} }
PublishedFile:
- { actions: [publish_clipboard], filters: {} }
default_value_tk-nuke:
Task:
- { actions: [assign_task, task_to_ip], filters: {} }
Version:
- { actions: [quicktime_clipboard, sequence_clipboard], filters: {} }
PublishedFile:
- { actions: [publish_clipboard], filters: {} }
- { actions: [read_node], filters: { published_file_type: Rendered Image} }
- { actions: [script_import], filters: { published_file_type: Nuke Script} }
Sparse configurations
In the configuration you can define default value for the various app settings that you define.
As of v0.18.x
, these values will be read at run-time if the setting is not
specified in the environment. Prior to v0.18.x
, configuration files were required to be
non-sparse, meaning that all parameters needed to be populated in the configuration.
Default values per engine
It is possible to define defaults per engine using the following syntax:
setting_name:
type: some_type
description: "description of the setting"
default_value: general_default
default_value_tk-nuke: nuke_default
default_value_tk-maya: maya_default
When the app is being installed or updated, toolkit will first look for a default value
for the engine which the app is being installed into. If that is not found, it will look
for a general default_value
setting. If that is not found, the user will be prompted to
manually supply a value to populate in the environment. As of v0.18.x
,
settings with a default value will not be explicitly added to the installed/updated
app’s settings.
Values that are procedurally populated by hooks
For advanced use cases, it is possible to specify a configuration value as a special hook evaluator rather than as a template setting. This means that when you configure your environment, rather than specifying an actual value for a setting to use with your app, you can specify a piece of code that returns the value to use. This makes it possible to create very complex settings logic. For more information, see the example_template_hook.py hook located in the core hooks area.
Simple Data Types
A number of simple data types are supported. These do not have any special options.
str
- a string valueint
- an integerfloat
- a floating point valuebool
- a boolean, expecting values true or false
An example declaration for a simple data type may look like this:
debug_logging:
type: bool
default_value: false
description: Controls whether debug messages should be emitted to the logger
The config_path data type
Use this when your app requires an external file that is part of the configuration. Typically, this settings type is used when you want to allow for a user to associate files with a configuration. These files can be icons or other resource files that should be part of the configuration. These paths should always be defined without an initial slash and using slashes as its path separator. Sgtk will translate it into a valid Windows path:
output_icon:
type: config_path
description: A config centric path that points to a square icon png file.
The publish_type data type
Use this when you want a setting which expects a Publish Type - these are typically used
when publishing data to Flow Production Tracking. Value is a string matching PublishedFileType.code
:
published_script_type:
type: publish_type
description: The Published file type for published Nuke scripts.
Note
The equivalent tank_type
data type is also supported for backwards compatibility.
The shotgun_entity_type data type
Value is a string that represents a Flow Production Tracking entity type like Task, Sequence, Shot:
entity_type:
type: shotgun_entity_type
default_value: Shot
description: "The entity type to attach to"
The shotgun_permission_group data type
Value is a string that represents the display name of a Flow Production Tracking permission group like Admin, Artist:
permissions_group:
type: shotgun_permission_group
default_value: Artist
description: "Permissions group to use when performing the operation"
The shotgun_filter data type
Value is a filter that will be passed to the shotgun api when performing a query (e.g. ["sg_status_list", "is", "cmpt"]
).
As shotgun filters can take multiple forms, no additional validation will be done on values of this type:
publish_filters:
type: list
values:
type: shotgun_filter
The template data type
Value is a string matching an entry in templates.yml
. Using the fields option
you specify how Toolkit should validate the template that the user specifies:
output_render:
description: Render output location
type: template
fields: "context, name, channel, version, [width], [height], [eye]"
When you specify which fields your app requires, it must be strict. Toolkit will ensure
that the template that the user has chosen to configure the app with exactly matches the
values specified in the fields string. Typically, the app will also pull in fields
via the context object - you can specify this by using the special context
parameter.
Optional parameters are represented with [brackets]
and the special token *
indicates
that an arbitrary number of fields is okay. For example:
fields: name
- only templates containing a single field name will be validfields: context, name
- templates containing exactly the number of fields that the context is able to resolve, and name, will be valid.fields: context, name, [width], [height]
- same as previous but the two fields width and height can be present in the template at the user’s discression.fields: name, *
- name is required, the rest of the fields can be arbitrary.
A typical example illustrating the use of *
is when you need to look for things in the
scene which do not belong to the current context - for example if you are working on a shot
and wants to scan for assets that have been imported into the scene. In this case, you cannot
use the context since this is pointing at the current Shot:
input_templates_to_look_for:
type: list
description: List of templates to look for when scanning the scene for inputs.
A template listed here only needs to have a field called version. This field
will be used in the comparison to determine what is out of date.
values:
type: template
fields: "version, *"
Empty settings
By default, all template settings require the user to specify a valid template when
they are added to the environment configuration. If you want to define a template
setting that can be set to None
, specify the allows_empty option and set it to True:
output_render:
description: Render output location, Leave blank for default output.
type: template
fields: "context, name, channel, version, [width], [height], [eye]"
allows_empty: True
The hook data type
Hooks makes it possible to create flexible and powerful configurations for toolkit apps. A hook is a python file which contains a single class (deriving from a Hook base class or from other hook classes) and a number of pre-defined methods.
Hooks takes configuration beyond the retrival of a simple string or int value and allows apps to break out parts of the code that are likely to vary across facilities into code that can be customized, either by completely overriding it or by deriving from another hook class.
For hook documentation, see Hooks.
Typical Workflow for hooks
If you are using a Toolkit app and want to customize the behaviour that is defined in a hook, you can typically follow these steps:
Identify the hook file and copy it into the
hooks
folder in your Toolkit project configuration. For example, if your hook is namedvalidation.py
, copy it to the configuration folder asCONFIG_ROOT/hooks/validation.py
.In your environment configuration, find the app config and tell the app to use your new file instead of the default one:
# change the default value in your env config: validation_hook: {self}/validation.py # to this new value: validation_hook: {config}/validation.py
Now we have a bunch of duplicated code in our config folder which isn’t great. Because we are inheriting from the default hook, our custom hook only really needs to contain the modifications that we are making and not the entire logic. So go in and strip back our custom hook in the config folder:
# customized validation hook in CONFIG_ROOT/hooks/validation.py import sgtk HookBaseClass = sgtk.get_hook_baseclass() class Validator(HookBaseClass): def validate_string(self, value): # call the built-in hook that comes with the app first HookBaseClass.validate_string(self, value) # now add our extra bit of validation if "LOLCAT" in value: raise Exception("No lol-cats in my pipeline!")
This makes it possible to customize parts of hooks, while leaving a lot of the hook code in tact. This keeps customizations lightweight, avoids code duplication and allows an app developer to push out bug fixes much more easily.
Multiple levels of inheritance in hooks
For even more complex scenarios, it is possible to declare an inheritance chain in the environment config:
validation_hook: {$STUDIO_ROOT}/studio_validation.py:{config}/project_validation.py.
This example will have three levels of inheritance: first the built in hook, after that a studio level implementation defined by an environment variable and lastly a project level implementation.
Accessing a framework inside a hook
For even more complex hook setups, you can also access frameworks from inside your hooks:
class SomeHook(HookBaseClass):
def some_method(self):
# first get a framework handle. This object is similar to an app or engine object
fw = self.load_framework("my-framework-library_v123.x.x")
# now just like with an app or an engine, if you want to access code in the python
# folder, you can do import_plugin
module = fw.import_module("some_module")
Note how we are accessing the framework instance my-framework-library_v123.x.x above. This needs to be defined in the currently running environment, as part of the frameworks section:
engines:
# all engine and app defs here...
frameworks:
# define the framework that we are using in the hook
my-framework-library_v123.x.x:
location: {type: dev, path: /some/path}
The list data type
Value is a list, all values in the list must be of the same data type. You must supply values dict that describes the data type of the list items:
entity_types:
type: list
values: {type: shotgun_entity_type}
default_value: [Sequence, Shot, Asset, Task]
description: List of Flow Production Tracking entity types where this
Sgtk action should be visible on the Actions menu.
Optionally you can also specify an allows_empty option if an empty list is a valid value:
publish_types:
type: list
allows_empty: True
values: {type: pubish_type}
You would then be able to specify an empty list in your environment configuration:
apps:
tk-multi-loader:
tank_types: []
The dict data type
Value is a dictionary, keys are always strings, values can be of mixed types.
In info.yml, optionally provide an items dict. Used to mark what keys must be in the config, default values, and type info for the values which will be used for validation:
write_nodes:
type: list
description: "A list of dictionaries in which you define the Sgtk write nodes that
are supported in this configuration."
allows_empty: True
values:
type: dict
items:
name: { type: str }
file_type: { type: str }
publish_type: { type: tank_type }
render_template:
type: template
fields: "context, name, channel, version, SEQ, [width], [height], [eye]"
A valid setting could look like this:
movies:
- {name: Exr Render, file_type: exr, publish_type: Quicktime, render_template: exr_shot_render}
- {name: Dpx Render, file_type: dpx, publish_type: Quicktime, render_template: dpx_shot_render}
Using QT inside your Toolkit App
You can use QT classes inside your app code. Sgtk will handle the import and gracefully manage different platform considerations in the background. Typically, PySide is being used for QT integration, but Sgtk may use PyQT in certain engines. Normally, the code is pretty portable between the two systems and it should be no problem writing code that works with both libraries.
In order to use QT, import it from Sgtk:
from sgtk.platform.qt import QtCore, QtGui
Toolkit will make sure Qt is sourced in the correct way. Keep in mind that many applications (for example Nuke) may not have a functional Qt that can be imported when they run in batch mode.
Using QT 5 inside your Toolkit App
When running in an environment with PySide 2 , Toolkit will provide under sgtk.platform.qt
an
emulation layer that mimics the PySide 1 API on top of PySide 2 so that your Toolkit applications don’t
need to be updated to work in those environments. If you need straight access to the PySide 2 API, you
can access it through the sgtk.platform.qt5
module. Detecting the availability of Qt 5 can be done
through Engine.has_qt5()
.
Warning
The PySide 1 emulation layer for PySide 2 may be missing some features. It provides enough coverage to run the officially supported Toolkit applications. If you find that something in the PySide 1 emulation layer is not working or missing, please contact https://knowledge.autodesk.com/contact-support so that we may update it.
To learn more about API discrepancies between Qt 4 and Qt 5, you can visit https://wiki.qt.io/Transition_from_Qt_4.x_to_Qt5.
Creating Dialogs
When creating a dialog, it is important to parent it properly to the host environment. There is nothing stopping you from managing this by yourself, but for maximum compatibility and portabilty, we strongly suggest that you l et Toolkit handle it. When using Sgtk to set up your UI, just let your UI class derive from QtGui.QWidget and pass it to one of the UI factory methods that the engine has. For example:
from sgtk.platform.qt import QtCore, QtGui
# derive from QtGui.QWidget for your UI components.
class AppDialog(QtGui.QWidget):
def __init__(self, param1, param2):
QtGui.QWidget.__init__(self)
# the engine is then used to correctly launch this dialog. In your app code
# you can now do create a window using the engine's factory methods.
# display widget in a modeless window:
widget_obj = self.engine.show_dialog("Dialog Title", self, AppDialog, param1, param2)
# display widget in a modal dialog - blocking call
(return_code, widget_obj) = self.engine.show_modal("Dialog Title", self, AppDialog, param1, param2)
What happens in the above calls is that your app widget is parented inside of a Dialog window Sgtk is creating. Sgtk will add additional potential window constructs, menus etc. Whenever the app widget is closed (for example using the close() method), the parent window that is used to wrap the widget will automatically close too.
Modal dialogs and exit codes
If you want to run your widget as a modal dialog, it may be useful to signal success or failure.
This is normally done in QT using the methods QDialog.accepted() and QDialog.rejected(), however since the app
widget typically derives from QWidget, these methods are not available. Instead, Sgtk will look for a member
property called exit_code
. Typically, your code for a modal dialog would look something like this:
def on_ok_button_clicked(self):
# user clicked ok
self.exit_code = QtGui.QDialog.Accepted
self.close()
def on_cancel_button_clicked(self):
# user clicked cancel
self.exit_code = QtGui.QDialog.Rejected
self.close()
The call to self.engine.show_modal() will return the appropriate status code depending on which button was clicked.
Hiding the default Toolkit title bar
By default, the standard Toolkit UI dialog includes a title bar at the top. However, sometimes this is not desirable,
especially when the contained widget is quite small. To hide the title bar, just add a property called
hide_tk_title_bar
to your widget class and set it to a value of True, for example:
class MyWidget(QtGui.QWidget):
@property
def hide_tk_title_bar(self):
return True
def __init__(self):
...
Styling your Toolkit App
If a standard Qt style sheet style.qss
file resides in the root of your app, Toolkit will automatically load it and apply it to your app.
When developping your app, you can enable interactive styling by setting the SHOTGUN_QSS_FILE_WATCHER
environment variable to 1
. Toolkit will automatically reload and re-apply the styling when the file is changed.
Note
The style sheet file watching can be helpful when developping apps, but shouldn’t be used in production.