def get_current_frame_range(self): """ get_current_frame_range will execute the hook specified in the 'hook_frame_operation' setting for this app. It will record the result of the hook and return the values as a dict of (str: int). If there is an internal exception thrown from the hook, it will reraise the exception as a tank.TankError and write the traceback to the log. If the data returned is not in the correct format, dict with at least two keys, it will also throw a tank.TankError exception. :returns: data (dict of str: int): head_in, in, out, tail_out :rtype: dict() :raises: tank.TankError """ try: result = self.execute_hook_method("hook_frame_operation", "get_frame_range") except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while getting the frame range: {}". format(str(err))) if not isinstance(result, dict) or (isinstance(result, dict) and len(result) < 2): raise tank.TankError( "Unexpected type returned from 'hook_frame_operation' for operation get_" "frame_range - expected a 'dictionary' with in_frame, out_frame values but " "returned '{} {}".format(result, (type(result).__name__)), result, ) return result
def init_engine(self): self.log_debug("%s: Initializing..." % self) # TODO: check Modo version info current_os = "linux64" #cmds.about(operatingSystem=True) if current_os not in ["mac", "win64", "linux64"]: raise tank.TankError( "The current platform is not supported! Supported platforms " "are Mac, Linux 64 and Windows 64.") if self.context.project is None: # must have at least a project in the context to even start! raise tank.TankError( "The engine needs at least a project in the context " "in order to start! Your context: %s" % self.context) self.log_debug("%s: Setting project..." % self) # Set the Modo project based on config self._set_project() # add qt paths and dlls self._init_pyside() # default menu name is Shotgun but this can be overriden # in the configuration to be Sgtk in case of conflicts self._menu_name = "Shotgun" if self.get_setting("use_sgtk_as_menu_name", False): self._menu_name = "Sgtk" # need to watch some scene events in case the engine needs rebuilding: cb_fn = lambda en=self.instance_name, pc=self.context, mn=self._menu_name: on_scene_event_callback( en, pc, mn) self.__watcher = SceneEventWatcher(cb_fn) self.log_debug("Registered open and save callbacks.")
def get_entity(self, entity_type=None): """ get entity from scene :returns: data (dict of str: int) :rtype: dict() :raises: tank.TankError """ try: result = self.execute_hook_method("hook_frame_operation", "get_entity", entity_type=entity_type) except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while getting the entity: {}".format( str(err))) if not isinstance(result, dict): raise tank.TankError( "Unexpected type returned from 'hook_frame_operation' for operation get_" "entity - expected a 'dictionary' with 'type' and 'id{} {}". format(result, (type(result).__name__)), result, ) return result
def get_scene_filename(self): """ get scene filename from current scene :returns: unicode :rtype: unicode :raises: tank.TankError """ try: result = self.execute_hook_method("hook_frame_operation", "get_scene_filename") except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while getting the scene filename: {}". format(str(err))) if not isinstance(result, unicode): raise tank.TankError( "Unexpected type returned from 'hook_frame_operation' for operation get_" "scene_filename - expected a string'{} {}".format( result, (type(result).__name__)), result, ) return result
def setup(launcher, context): extra_configs = launcher.get_setting("extra", {}) # Get the path to the python executable python_setting = { "darwin": "mac_python_path", "win32": "windows_python_path" }[sys.platform] python_path = extra_configs.get(python_setting) if not python_path: raise tank.TankError( "Your photoshop app launch config is missing the extra setting %s" % python_setting) # get the path to extension manager manager_setting = { "darwin": "mac_extension_manager_path", "win32": "windows_extension_manager_path" }[sys.platform] manager_path = extra_configs.get(manager_setting) if not manager_path: raise tank.TankError( "Your photoshop app launch config is missing the extra setting %s!" % manager_setting) os.environ["SGTK_PHOTOSHOP_EXTENSION_MANAGER"] = manager_path # make sure the extension is up to date try: import photoshop_extension_manager photoshop_extension_manager.update() except Exception, e: raise tank.TankError( "Could not run the Adobe Extension Manager. Please double check your " "Shotgun Pipeline Toolkit Photoshop Settings. Error Reported: %s" % e)
def get_frame_range_from_shotgun(self): """ Returns (in, out) frames from shotgun. """ # we know that this exists now (checked in init) entity = self.context.entity sg_entity_type = self.context.entity["type"] sg_filters = [["id", "is", entity["id"]]] sg_in_field = self.get_setting("sg_in_frame_field") sg_out_field = self.get_setting("sg_out_frame_field") fields = [sg_in_field, sg_out_field] data = self.shotgun.find_one(sg_entity_type, filters=sg_filters, fields=fields) # check if fields exist! if sg_in_field not in data: raise tank.TankError( "Configuration error: Your current context is connected to a Shotgun " "%s. This entity type does not have a " "field %s.%s!" % (sg_entity_type, sg_entity_type, sg_in_field)) if sg_out_field not in data: raise tank.TankError( "Configuration error: Your current context is connected to a Shotgun " "%s. This entity type does not have a " "field %s.%s!" % (sg_entity_type, sg_entity_type, sg_out_field)) return (data[sg_in_field], data[sg_out_field])
def get_current_frame_range(self): """ get_current_frame_range will execute the hook specified in the 'hook_frame_operation' setting for this app. It will record the result of the hook and return the values as a tuple of (in, out). If there is an internal exception thrown from the hook, it will reraise the exception as a tank.TankError and write the traceback to the log. If the data returned is not in the correct format, tuple with two keys, it will also throw a tank.TankError exception. :returns: Tuple of (in, out) frame range values. :rtype: tuple[int,int] :raises: tank.TankError """ try: result = self.execute_hook_method("hook_frame_operation", "get_frame_range") except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while getting the frame range: {}".format( str(err) ) ) if not isinstance(result, tuple) or ( isinstance(result, tuple) and len(result) != 2 ): raise tank.TankError( "Unexpected type returned from 'hook_frame_operation' for operation get_" "frame_range - expected a 'tuple' with (in_frame, out_frame) values but " "returned '%s' : %s" % (type(result).__name__, result) ) return result
def get_editorial_data_from_shotgun(self): """ get_editorial_data_from_shotgun will query shotgun for the 'sg_in_frame_field', 'sg_out_frame_field', 'sg_frame_rate_field' setting values and return a tuple of (in, out, frame_rate). If the fields specified in the settings do not exist in your Shotgun site, this will raise a tank.TankError letting you know which field is missing. :returns: Tuple of (in, out, frame_rate) :rtype: tuple[int,int,float] :raises: tank.TankError """ # we know that this exists now (checked in init) entity = self.context.entity project = self.context.project sg_entity_type = self.context.entity["type"] sg_filters = [["id", "is", entity["id"]]] sg_in_field = self.get_setting("sg_in_frame_field") sg_out_field = self.get_setting("sg_out_frame_field") sg_frame_rate_field = self.get_setting("sg_frame_rate_field") fields = [sg_in_field, sg_out_field, sg_frame_rate_field] data = self.shotgun.find_one(sg_entity_type, filters=sg_filters, fields=fields) # check if fields exist! if sg_in_field not in data: raise tank.TankError( "Configuration error: Your current context is connected to a Shotgun " "%s. This entity type does not have a " "field %s.%s!" % (sg_entity_type, sg_entity_type, sg_in_field)) if sg_out_field not in data: raise tank.TankError( "Configuration error: Your current context is connected to a Shotgun " "%s. This entity type does not have a " "field %s.%s!" % (sg_entity_type, sg_entity_type, sg_out_field)) if not data.get(sg_frame_rate_field): proj_data = self.shotgun.find_one( "Project", filters=[["id", "is", project["id"]]], fields=fields) if sg_frame_rate_field not in proj_data: data[sg_frame_rate_field] = None else: data[sg_frame_rate_field] = proj_data[sg_frame_rate_field] return (data[sg_in_field], data[sg_out_field], data[sg_frame_rate_field])
def init_engine(self): """ Main initialization entry point. """ self.log_debug("%s: Initializing..." % self) if hou.applicationVersion()[0] < 12: raise tank.TankError( "Your version of Houdini is not supported. Currently, Toolkit only supports version 12+" ) # Support OS X on 14+ only if sys.platform == "darwin" and hou.applicationVersion()[0] < 14: raise tank.TankError( "Your version of Houdini is not supported on OS X. Currently, " "Toolkit only supports version 14+ on OS X.") try: hou_ver_str = ".".join([str(v) for v in hou.applicationVersion()]) self.log_user_attribute_metric("Houdini version", hou_ver_str) except: # ignore all errors. ex: using a core that doesn't support metrics pass # keep track of if a UI exists self._ui_enabled = hasattr(hou, 'ui') # pyside is integrated as of houdini 14. if hou.applicationVersion()[0] >= 14: self._integrated_pyside = True self._ui_type = "PySide" self.log_debug("Using integrated PySide.") else: self._integrated_pyside = False self._ui_type = None # add our built-in pyside to the python path when on windows if not self._integrated_pyside and sys.platform == "win32": py_ver = sys.version_info[0:2] if py_ver == (2, 6): pyside_path = os.path.join(self.disk_location, "resources", "pyside112_py26_win64") sys.path.append(pyside_path) self.log_debug("Using bundled PySide: %s" % (pyside_path, )) elif py_ver == (2, 7): pyside_path = os.path.join(self.disk_location, "resources", "pyside121_py27_win64") sys.path.append(pyside_path) self.log_debug("Using bundled PySide: %s" % (pyside_path, )) else: self.log_warning("PySide not bundled for python %d.%d" % (py_ver[0], py_ver[1]))
def unset_open_file_callback(self, func, callback): """ unset_open_file_callback will execute the hook specified in the 'hook_frame_operation' setting for this app. It will unset a callback on file open if the dcc supports it. If there is an internal exception thrown from the hook, it will reraise the exception as a tank.TankError and write the traceback to the log. If the data returned is not in the correct format, tuple with two keys, it will also throw a tank.TankError exception. :param func func: The function to unset as the callback. :param callback: whatever the set_open_file_callback returned should be set here in case the DCC needs it to unset the callback. :raises: tank.TankError """ try: self.execute_hook_method("hook_callbacks", "unset_open_file_callback", func=func) except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while setting open file callback: {}". format(str(err)))
def set_frame_range(self, in_frame, out_frame): """ set_current_frame_range will execute the hook specified in the 'hook_frame_operation' setting for this app. It will pass the 'in_frame' and 'out_frame' to the hook. If there is an internal exception thrown from the hook, it will reraise the exception as a tank.TankError and write the traceback to the log. :param int in_frame: The value of in_frame that we want to set in the current session. :param int out_frame: The value of out_frame that we want to set in the current session. :raises: tank.TankError """ try: self.execute_hook_method( "hook_frame_operation", "set_frame_range", in_frame=in_frame, out_frame=out_frame, ) except Exception as err: error_message = traceback.format_exc() self.logger.error(error_message) raise tank.TankError( "Encountered an error while setting the frame range: {}".format( str(err) ) )
def init_engine(self): """ Main initialization entry point. """ self.log_debug("%s: Initializing..." % self) if hou.applicationVersion()[0] < 12: raise tank.TankError( "Your version of Houdini is not supported. Currently, Toolkit only supports version 12+" ) # keep track of if a UI exists self._ui_enabled = hasattr(hou, 'ui') # add our built-in pyside to the python path when on windows if sys.platform == "win32": py_ver = sys.version_info[0:2] if py_ver == (2, 6): pyside_path = os.path.join(self.disk_location, "resources", "pyside112_py26_win64") sys.path.append(pyside_path) elif py_ver == (2, 7): pyside_path = os.path.join(self.disk_location, "resources", "pyside121_py27_win64") sys.path.append(pyside_path) else: self.log_warning("PySide not bundled for python %d.%d" % (py_ver[0], py_ver[1]))
def _download_thumbnail(self, data): url = data["url"] # first check in our thumbnail cache url_obj = urlparse.urlparse(url) url_path = url_obj.path path_chunks = url_path.split("/") path_chunks.insert(0, self._app.cache_location) # now have something like ["/studio/proj/tank/cache/tk-framework-widget", "", "thumbs", "1", "2", "2.jpg"] # treat the list of path chunks as an arg list path_to_cached_thumb = os.path.join(*path_chunks) if os.path.exists(path_to_cached_thumb): # cached! sweet! return {"thumb_path": path_to_cached_thumb} # ok so the thumbnail was not in the cache. Get it. temp_directory = tempfile.mkdtemp() temp_file = os.path.join(temp_directory, path_chunks[-1]) tank.util.download_url(self._app.shotgun, url, temp_file) # now try to cache it try: self._app.ensure_folder_exists( os.path.dirname(path_to_cached_thumb)) shutil.copy(temp_file, path_to_cached_thumb) # modify the permissions of the file so it's writeable by others os.chmod(path_to_cached_thumb, 0666) except Exception, e: raise tank.TankError( "Could not cache thumbnail %s in %s. Error: %s" % (url, path_to_cached_thumb, e))
def get_new_context(self, script_path): """ Returns a new sgtk.context.Context for the given script path. If the context exists in the in-memory cache, then that is returned, otherwise a new Context object is constructed, cached, and returned. :param script_path: The path to a script file on disk. """ context = self._context_cache.get(script_path) if context: return context try: context = self._get_context_from_script(script_path) if context: self._context_cache[script_path] = context return context else: raise tank.TankError( "Toolkit could not determine the context associated with this script." ) except Exception as e: self.engine.menu_generator.create_sgtk_disabled_menu(e) self.engine.logger.debug(e) return None
def __getattr__(self, name): raise tank.TankError( "Looks like you are trying to run an App that uses a QT " "based UI, however the Shell engine could not find a PyQt " "or PySide installation in your python system path. We " "recommend that you install PySide if you want to " "run UI applications from the Shell.")
def _on_screenshot(self): """ Perform the actual screenshot """ # hide the containing window - we can't actuall hide # the window as this will break modality! Instead # we have to move the window off the screen: win = self._safe_get_dialog() win_geom = None if win: win_geom = win.geometry() win.setGeometry(1000000, 1000000, win_geom.width(), win_geom.height()) # make sure this event is processed: QtCore.QCoreApplication.processEvents() QtCore.QCoreApplication.sendPostedEvents(None, 0) QtCore.QCoreApplication.flush() path = None pm = None try: # get temporary file to use: # to be cross-platform and python 2.5 compliant, we can't use # tempfile.NamedTemporaryFile with delete=False. Instead, we # use tempfile.mkstemp which does practically the same thing! tf, path = tempfile.mkstemp(suffix=".png", prefix="tanktmp") if tf: os.close(tf) # do screenshot with thread so we don't block anything screenshot_thread = ThumbnailWidget.ScreenshotThread(path) screenshot_thread.start() while not screenshot_thread.isFinished(): screenshot_thread.wait(100) QtGui.QApplication.processEvents() er = screenshot_thread.get_error() if er: raise tank.TankError("Failed to capture screenshot: %s" % er) # load into pixmap: pm = QtGui.QPixmap(path) finally: # restore the window: if win: win.setGeometry(win_geom) QtCore.QCoreApplication.processEvents() # remove the temporary file: if path and os.path.exists(path): os.remove(path) return pm
def pre_app_init(self): """ Do any additional initialization before the apps are loaded. """ self.log_debug("%s: Initializing..." % self) # check max version max_major_version = mxs.maxVersion()[0] # 13000 means 2011, 14000 means 2012, etc. if max_major_version not in (13000, 14000, 15000, 16000): raise tank.TankError( "Unsupported version of 3ds Max! The engine only works with " "versions 2011, 2012, 2013* & 2014. (* Please see the engine " "documentation for more details regarding 2013)") elif max_major_version == 15000: # specifically for 2013 which is not officially supported but may work, output a warning: self.log_warning( "This version of 3ds Max is not officially supported by Toolkit and you may " "experience instability. Please contact [email protected] " "if you do have any issues.") # set up a qt style sheet # note! - try to be smart about this and only run # the style setup once per session - it looks like # 3dsmax slows down if this is executed every engine restart. qt_app_obj = tank.platform.qt.QtCore.QCoreApplication.instance() curr_stylesheet = qt_app_obj.styleSheet() if "toolkit 3dsmax style extension" not in curr_stylesheet: self._initialize_dark_look_and_feel() # note! For some reason it looks like the widget background color isn't being set correctly # on 3dsmax top level items. In order to mitigate this, apply a style to set the background # color on the main app window area. The main app window area is typically a QWidget which # is a child of a QDialog (created by tk-core) with the name TankDialog. Based on this, # we can construct a style directive QDialog#TankDialog > QWidget which applies to the # immediate QWidget children only. curr_stylesheet += "\n\n /* toolkit 3dsmax style extension */ \n\n" curr_stylesheet += "\n\n QDialog#TankDialog > QWidget { background-color: #343434; }\n\n" qt_app_obj.setStyleSheet(curr_stylesheet) self._safe_dialogs = [] engine = self class DialogEvents(tank.platform.qt.QtCore.QObject): def eventFilter(self, obj, event): # Remove from tracked dialogs if event.type() == tank.platform.qt.QtCore.QEvent.Close: if obj in engine._safe_dialogs: engine._safe_dialogs.remove(obj) return False self.dialogEvents = DialogEvents()
def init_app(self): """ Called as the application is being initialized """ if self.context.entity is None: raise tank.TankError("Cannot load the Set Frame Range application! " "Your current context does not have an entity (e.g. " "a current Shot, current Asset etc). This app requires " "an entity as part of the context in order to work.")
def init_app(self): """ App entry point """ # make sure that the context has an entity associated - otherwise it wont work! if self.context.entity is None: raise tank.TankError("Cannot load the Set Frame Range application! " "Your current context does not have an entity (e.g. " "a current Shot, current Asset etc). This app requires " "an entity as part of the context in order to work.") self.engine.register_command("Sync Frame Range with Shotgun", self.run_app)
def _on_screenshot(self): """ Perform the actual screenshot """ # hide the containing window - we can't actuall hide # the window as this will break modality! Instead # we have to move the window off the screen: win = self._safe_get_dialog() win_geom = None if win: win_geom = win.geometry() win.setGeometry(1000000, 1000000, win_geom.width(), win_geom.height()) # make sure this event is processed: QtCore.QCoreApplication.processEvents() path = None pm = None try: # get temporary file to use: with tempfile.NamedTemporaryFile(suffix=".png", prefix="tanktmp", delete=False) as temp_file: path = temp_file.name # do screenshot with thread so we don't block anything screenshot_thread = ThumbnailWidget.ScreenshotThread(path) screenshot_thread.start() while not screenshot_thread.isFinished(): screenshot_thread.wait(100) QtGui.QApplication.processEvents() er = screenshot_thread.get_error() if er: raise tank.TankError("Failed to capture screenshot: %s" % er) # load into pixmap: pm = QtGui.QPixmap(path) finally: # restore the window: if win: win.setGeometry(win_geom) QtCore.QCoreApplication.processEvents() # remove the temporary file: if path: os.remove(path) return pm
def init_engine(self): self.log_debug("%s: Initializing..." % self) if self.context.project is None: # must have at least a project in the context to even start! raise tank.TankError("The Motionbuilder engine needs at least a project in the context " "in order to start! Your context: %s" % self.context) # import pyside QT UI libraries self._init_pyside() # motionbuilder doesn't have good exception handling, so install our own trap sys.excepthook = tank_mobu_exception_trap
def init_engine(self): """ Main initialization entry point. """ self.logger.debug("%s: Initializing..." % self) if hou.applicationVersion()[0] < 14: raise tank.TankError( "Your version of Houdini is not supported. Currently, Toolkit " "only supports version 14+.") # keep track of if a UI exists self._ui_enabled = hasattr(hou, 'ui')
def pre_app_init(self): """ Initializes the Harmony engine. """ self.logger.debug("Initializing engine... %s", self) self.tk_harmony = self.import_module("tk_harmony") self.init_qt_app() port = os.environ.get("SGTK_HARMONY_ENGINE_PORT") host = os.environ.get("SGTK_HARMONY_ENGINE_HOST", "127.0.0.1") self.logger.debug("host: %s", host) self.logger.debug("port: %s", port) application_client_class = self.tk_harmony.application.Application self.logger.debug(" application_client_class: %s " % application_client_class) self._dcc_app = application_client_class( self, parent=self._qt_app_central_widget, host=host, port=int(port)) self.logger.debug(" self._dcc_app: %s " % self._dcc_app) self._dcc_app.register_callback("SHOW_MENU", self.on_show_menu) self._dcc_app.register_callback("NEW_PROJECT_CREATED", self.on_new_project_created) self._dcc_app.register_callback("PROJECT_OPENED", self.on_project_opened) self._dcc_app.register_callback("PING", self.on_ping) self._dcc_app.register_callback("QUIT", self.on_app_quit) # check that we are running an ok version of Harmony current_os = sys.platform if current_os not in ["darwin", "win32", "linux64"]: raise tank.TankError("The current platform is not supported!" " Supported platforms " "are Mac, Linux 64 and Windows 64.") # default menu name is Shotgun but this can be overriden # in the configuration to be Sgtk in case of conflicts self._menu_name = "Shotgun" if self.get_setting("use_sgtk_as_menu_name", False): self._menu_name = "Sgtk"
def init_engine(self): self.log_debug("%s: Initializing..." % self) self.toolkit_rv_mode_name = os.environ["TK_RV_MODE_NAME"] if self.context.project is None: # must have at least a project in the context to even start! raise tank.TankError("The engine needs at least a project in the context " "in order to start! Your context: %s" % self.context) # default menu name is Shotgun but this can be overriden # in the configuration to be Sgtk in case of conflicts self._menu_name = "Shotgun" if self.get_setting("use_sgtk_as_menu_name", False): self._menu_name = "SGTK" self._ui_enabled = True
def init_app(self): """ App entry point """ # make sure that the context has an entity associated - otherwise it wont work! if self.context.entity is None: raise tank.TankError( "Cannot load the Set Frame Range application! " "Your current context does not have an entity (e.g. " "a current Shot, current Asset etc). This app requires " "an entity as part of the context in order to work." ) # We grab the menu name from the settings so that the user is able to register multiple instances # of this app with different frame range fields configured. self.engine.register_command(self.get_setting("menu_name"), self.run_app)
def get_current_frame_range(self, engine): if engine == "tk-maya": import pymel.core as pm import maya.cmds as cmds current_in = cmds.playbackOptions(query=True, minTime=True) current_out = cmds.playbackOptions(query=True, maxTime=True) elif engine == "tk-nuke": import nuke current_in = int(nuke.root()["first_frame"].value()) current_out = int(nuke.root()["last_frame"].value()) elif engine == "tk-motionbuilder": from pyfbsdk import FBPlayerControl, FBTime lPlayer = FBPlayerControl() current_in = lPlayer.LoopStart.GetFrame() current_out = lPlayer.LoopStop.GetFrame() elif engine == "tk-softimage": import win32com xsi = win32com.client.Dispatch('XSI.Application') current_in = xsi.GetValue("PlayControl.In") current_out = xsi.GetValue("PlayControl.Out") elif engine == "tk-houdini": import hou current_in, current_out = hou.playbar.playbackRange() elif engine == "tk-3dsmax": from Py3dsMax import mxs current_in = mxs.animationRange.start current_out = mxs.animationRange.end elif engine == "tk-3dsmaxplus": import MaxPlus ticks = MaxPlus.Core.EvalMAXScript("ticksperframe").GetInt() current_in = MaxPlus.Animation.GetAnimRange().Start() / ticks current_out = MaxPlus.Animation.GetAnimRange().End() / ticks else: raise tank.TankError( "Don't know how to get current frame range for engine %s!" % engine) return (current_in, current_out)
def init_app(self): """ App entry point """ # make sure that the context has an entity associated - otherwise it wont work! if self.context.entity is None: raise tank.TankError( "Cannot load the AutoLoadScene application! " "Your current context does not have an entity (e.g. " "a current Shot, current Asset etc). This app requires " "an entity as part of the context in order to work.") app_name = self.get_setting("display_name") available_steps = self.get_setting("available_steps") if self.context.step['name'] in available_steps: self.engine.register_command(app_name, self.run_app)
def _get_context_from_script(self, script): """ Returns an sgtk.context.Context object from the given script path. :param script: The path to a script file on disk. """ tk = tank.tank_from_path(script) context = tk.context_from_path( script, previous_context=self.engine.context, ) if context.project is None: raise tank.TankError( "The Nuke engine needs at least a project " "context in order to start! Your context: %s" % context) else: return context
def init_engine(self): """ constructor """ self.log_debug("%s: Initializing..." % self) # check max version max_major_version = mxs.maxVersion()[0] # 13000 means 2011, 14000 means 2012, etc. if max_major_version not in (13000, 14000, 15000, 16000): raise tank.TankError( "Unsupported version of 3ds Max! The engine only works with " "versions 2011, 2012, 2013* & 2014. (* Please see the engine " "documentation for more details regarding 2013)") elif max_major_version == 15000: # specifically for 2013 which is not officially supported but may work, output a warning: self.log_warning( "This version of 3ds Max is not officially supported by Toolkit and you may " "experience instability. Please contact [email protected] " "if you do have any issues.")
def init_app(self): """ App entry point """ # make sure that the context has an entity associated - otherwise it wont work! try: if self.context.entity is None: raise tank.TankError( "Your current context does not have an entity (e.g. " "a current Shot, current Asset etc). This app requires " "an entity as part of the context in order to work.") display_name = self.get_setting( 'display_name') or 'Review in WebGL' self.engine.register_command( 'Review in WebGL', self.run_app, { 'short_name': 'tk-shotgun-webgl', 'title': display_name, 'supports_multiple_selection': False, "entity_types": ["Version"] }) except Exception as e: self.log_error(e)