Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ def eventFilter(self, obj, event):
# This needs to be present for apps as it will be used in
# show_dialog when perforce asks for login info very early on.
self.tk_3dsmax = self.import_module("tk_3dsmax")

# include callbacks to ensure context is correct
self.tk_3dsmax.tank_ensure_callbacks_registered(engine=self)

# The "qss_watcher" setting causes us to monitor the engine's
# style.qss file and re-apply it on the fly when it changes
Expand Down Expand Up @@ -605,6 +608,7 @@ def show_modal(self, title, bundle, widget_class, *args, **kwargs):
return None

status = QtGui.QDialog.DialogCode.Rejected
widget=None

try:
# Disable 'Shotgun' background menu while modals are there.
Expand All @@ -631,7 +635,8 @@ def show_modal(self, title, bundle, widget_class, *args, **kwargs):
self.tk_3dsmax.MaxScript.enable_menu()

# lastly, return the instantiated widget
return (status, widget)
if widget:
return (status, widget)

def safe_dialog_exec(self, func):
"""
Expand Down
13 changes: 9 additions & 4 deletions python/startup/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from __future__ import print_function
import os
import sys
from traceback import format_exc

import pymxs

Expand Down Expand Up @@ -74,7 +75,7 @@ def bootstrap_sgtk_with_plugins():

logger = sgtk.LogManager.get_logger(__name__)

logger.debug("Launching 3dsMax in plugin mode")
logger.debug("Launching additional startup scripts")

# Load all plugins by calling the 'load()' entry point.
for plugin_path in os.environ["SGTK_LOAD_MAX_PLUGINS"].split(os.pathsep):
Expand All @@ -89,6 +90,8 @@ def bootstrap_sgtk_with_plugins():
"Missing 'load()' method in plugin %s. Plugin won't be loaded"
% plugin_path
)
except:
logger.error(format_exc())


def bootstrap_sgtk():
Expand All @@ -114,11 +117,13 @@ def bootstrap_sgtk():
else:
error("Shotgun: Unknown platform - cannot setup ssl")
return


bootstrap_sgtk_classic()

#PHOSPHENE: i'm not sure what this would do, maybe it's leftover from an old method to instantiate 3dsmax?
#anyway we'll just combine the two calls, use the env variable to include additional startup scripts
if os.environ.get("SGTK_LOAD_MAX_PLUGINS"):
bootstrap_sgtk_with_plugins()
else:
bootstrap_sgtk_classic()

# clean up temp env vars
for var in [
Expand Down
186 changes: 186 additions & 0 deletions python/tk_3dsmax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,191 @@
# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights
# not expressly granted therein are reserved by Shotgun Software Inc.

import sgtk #@UnresolvedImport
import os
from traceback import format_exc

from .menu_generation import MenuGenerator
from .maxscript import MaxScript
from .warning_dialog import WarningDialog

import MaxPlus, pymxs #@UnresolvedImport

logger = sgtk.LogManager.get_logger(__name__)

g_tank_callbacks_registered = False

def PostOpenProcess(*args, **kwargs):
"""
Callback that fires every time a script is loaded.
"""
try:
logger.debug("SGTK Callback: script loaded")
# If we have opened a file then we should check if automatic
# context switching is enabled and change if possible
engine = sgtk.platform.current_engine()
file_name = _session_path()
logger.debug("Currently running engine: %s" % (engine,))
logger.debug("File name to load: '%s'" % (file_name,))

#if we already have a context and it matches the path, dont do anything else
curr_ctx = None
if engine:
curr_ctx = engine.context
new_ctx = engine.sgtk.context_from_path(file_name, curr_ctx)
if curr_ctx==new_ctx:
logger.debug("Shotgun reports context is already correct, not attempting to reset")
return

if file_name and engine is not None:
logger.debug("Will attempt to execute tank_from_path('%s')" % (file_name,))
try:
# todo: do we need to create a new tk object, instead should we just
# check that the context gets created correctly?
tk = sgtk.sgtk_from_path(file_name)
logger.debug("Instance '%s'is associated with '%s'" % (tk, file_name))
except sgtk.TankError as e:
error=format_exc().replace('\n', '<br />')
errorMessage="No tk instance associated with '%s':<br /><br /> %s<br /><br />%s" % (file_name, e, error)
friendlyMessage='<b>Could not determine a Shotgun Entitiy for '+os.path.basename(file_name)+",</b><br />"
friendlyMessage+='Please ensure you are in a Shotgun-aware instance of 3dsMax with the correct project and try again.'
logger.debug(errorMessage)
engine.show_modal('Error Initializing Shotgun',
engine,
WarningDialog,
message=friendlyMessage,
details=errorMessage)

#TODO: replace with command that just remove tools requiring specific context (ie Publish)
#but leave tools that can direct a user back to the correct context (ie Workfiles)
engine._remove_shotgun_menu()
return

# try to get current ctx and inherit its values if possible
curr_ctx = None
if sgtk.platform.current_engine():
curr_ctx = sgtk.platform.current_engine().context

logger.debug("")
new_ctx = tk.context_from_path(file_name, curr_ctx)
logger.debug("Current context: %r" % (curr_ctx,))
logger.debug("New context: %r" % (new_ctx,))
# Now switch to the context appropriate for the file
engine.change_context(new_ctx)

elif file_name and engine is None:

#NOTE that this is untested, as I was unable to determine when this sitatuation would be true

# we have no engine, this maybe because the integration disabled itself,
# due to a non Toolkit file being opened, prior to this new file. We must
# create a sgtk instance from the script path.
logger.debug("3dsmax file is already loaded but no tk engine running.")
logger.debug("Will attempt to execute tank_from_path('%s')" % (file_name,))
try:
tk = sgtk.sgtk_from_path(file_name)
logger.debug("Instance '%s'is associated with '%s'" % (tk, file_name))
except sgtk.TankError as e:
error=format_exc().replace('\n', '<br />')
errorMessage="No tk instance associated with '%s':<br /><br /> %s<br /><br />%s" % (file_name, e, error)
friendlyMessage='<b>Could not determine a Shotgun Entitiy for '+os.path.basename(file_name)+",</b><br />"
friendlyMessage+='Please ensure you are in a Shotgun-aware instance of 3dsMax with the correct project and try again.'
logger.debug(errorMessage)
engine.show_modal('Error Initializing Shotgun',
engine,
WarningDialog,
message=friendlyMessage,
details=errorMessage)

engine._remove_shotgun_menu()
return

new_ctx = tk.context_from_path(file_name)
logger.debug("New context: %r" % (new_ctx,))
# Now switch to the context appropriate for the file
engine.change_context(new_ctx)

except Exception:
error=format_exc()
logger.exception("An exception was raised during addOnScriptLoad callback.")
friendlyMessage='An unknown error occured attempting to initialize the Shotgun engine.'
logger.debug(error)
engine.show_modal('Error Initializing Shotgun',
engine,
WarningDialog,
message=friendlyMessage,
details=error)
return

def tank_ensure_callbacks_registered(engine=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

"""
Make sure that we have callbacks tracking context state changes.
The OnScriptLoad callback really only comes into play when you're opening a file or creating a new script, when
there is no current script open in your Nuke session. If there is a script currently open then this will spawn a
new Nuke instance and the callback won't be called.
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

global g_tank_callbacks_registered

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

logger.debug("Setting up 3dsMax Context Switching callbacks")

# we'll assume context switching is allowed for now
#if engine.get_setting("automatic_context_switch"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '

if not g_tank_callbacks_registered:
logger.debug("SGTK Registering onLoad callback")
MaxPlus.NotificationManager.Register(MaxPlus.NotificationCodes.FilePostOpen, PostOpenProcess)
g_tank_callbacks_registered = True
#else:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '

# we have an engine but the automatic context switching has been disabled, we should ensure the callbacks
# are removed.
# if g_tank_callbacks_registered:
#remove here

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '
unexpected indentation (comment)

# g_tank_callbacks_registered = False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

def _session_path():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

"""
Return the path to the current session
:return:
"""
if pymxs.runtime.maxFilePath and pymxs.runtime.maxFileName:
return os.path.join(pymxs.runtime.maxFilePath, pymxs.runtime.maxFileName)
else:
return None

def __engine_refresh(new_context):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

"""
Checks if the 3dsmax engine should be created or just have the context changed.
If an engine is already started then we just need to change context,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace

else we need to start the engine.
"""

engine_name = 'tk-3dsmax'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

curr_engine = sgtk.platform.current_engine()
if curr_engine:
# If we already have an engine, we can just tell it to change contexts
curr_engine.change_context(new_context)
else:
# try to create new engine
try:
logger.debug(
"Starting new engine: %s, %s, %s" % (
engine_name,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace

new_context.sgtk,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace

new_context
)
)
sgtk.platform.start_engine(engine_name, new_context.sgtk, new_context)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

except sgtk.TankEngineInitError as e:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local variable 'e' is assigned to but never used

# context was not sufficient! - disable tank!
logger.exception("Engine could not be started.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

#TODO: replace with command that just remove tools requiring specific context (ie Publish)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '

#but leave tools that can direct a user back to the correct context (ie Workfiles)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

#engine._remove_shotgun_menu()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line at end of file
blank line contains whitespace
too many blank lines (3)

32 changes: 32 additions & 0 deletions python/tk_3dsmax/warning_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'''

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Black would make changes.


simple dialog to raise warnings to user

'''

from sgtk.platform.qt import QtGui, QtCore #@UnresolvedImport

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline comment should start with '# '


#if we have an engine, we can simply use this class

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '

class WarningDialog(QtGui.QWidget):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace
indentation contains tabs

def __init__(self, message, details=None, *args, **kwargs):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs
over-indented


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace
indentation contains tabs

super(WarningDialog, self).__init__(*args, **kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs
over-indented


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace
indentation contains tabs

self.setObjectName("SGTK_WARNING_DIALOG")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs


self._label = QtGui.QLabel("<h3>"+message+"</h3>")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs
missing whitespace around arithmetic operator

self._label.setTextFormat(QtCore.Qt.RichText)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs

self._text = QtGui.QTextEdit()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs

self._text.setReadOnly(True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs

#self._text.setLineWrapMode(QtGui.QTextEdit.NoWrap)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '
indentation contains tabs

self._text.setText(str(details))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs


self._layout = QtGui.QVBoxLayout(self)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs

self._layout.addWidget(self._label)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs

self._layout.addWidget(self._text)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace
indentation contains tabs

#if we don't have an engine, we'll need to do some of the legwork ourselves

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block comment should start with '# '

def showWarningDialog(message, details=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace
indentation contains tabs

parent=QtGui.QApplication.activeWindow()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation contains tabs
local variable 'parent' is assigned to but never used
missing whitespace around operator
over-indented