1616
1717import pymxs
1818
19+ # Max versions compatibility constants
20+ VERSION_OLDEST_COMPATIBLE = 2023
21+ VERSION_OLDEST_SUPPORTED = 2024
22+ VERSION_NEWEST_SUPPORTED = 2025
23+ # Caution: make sure compatibility_dialog_min_version default value in info.yml
24+ # is equal to VERSION_NEWEST_SUPPORTED
25+
1926
2027class MaxEngine (sgtk .platform .Engine ):
2128 """
@@ -47,17 +54,13 @@ def host_info(self):
4754 }
4855
4956 References:
50- http ://docs .autodesk.com/3DSMAX/16/ ENU/3ds-Max-Python-API-Documentation/index.html
57+ https ://help .autodesk.com/view/MAXDEV/2026/ ENU/?guid=MAXDEV_Python_what_s_new_in_3ds_max_python_api_html
5158 """
5259 host_info = {"name" : "3ds Max" , "version" : "unknown" }
5360
54- try :
55- host_info ["version" ] = str (
56- self ._max_version_to_year (self ._get_max_version ())
57- )
58- except :
59- # Fallback to initialized values above
60- pass
61+ max_version_year = self .max_version_year
62+ if max_version_year :
63+ host_info ["version" ] = str (max_version_year )
6164
6265 return host_info
6366
@@ -73,6 +76,7 @@ def __init__(self, *args, **kwargs):
7376 self ._dock_widgets = []
7477
7578 self ._max_version = None
79+ self ._max_version_year = None
7680
7781 # proceed about your business
7882 sgtk .platform .Engine .__init__ (self , * args , ** kwargs )
@@ -98,35 +102,133 @@ def pre_app_init(self):
98102
99103 self .log_debug ("%s: Initializing..." % self )
100104
101- if self ._get_max_version () > MaxEngine .MAXIMUM_SUPPORTED_VERSION :
102- # Untested max version
105+ url_doc_supported_versions = "https://help.autodesk.com/view/SGDEV/ENU/?guid=SGD_si_integrations_engine_supported_versions_html"
106+
107+ if self .max_version_year < VERSION_OLDEST_COMPATIBLE :
108+ # Old incompatible version
109+ message = """
110+ Flow Production Tracking is no longer compatible with {product} versions older
111+ than {version}.
112+
113+ For information regarding support engine versions, please visit this page:
114+ {url_doc_supported_versions}
115+ """ .strip ()
116+
117+ if self .has_ui :
118+ try :
119+ QtGui .QMessageBox .critical (
120+ None , # parent
121+ "Error - Flow Production Tracking Compatibility!" .ljust (
122+ # Padding to try to prevent the dialog being insanely narrow
123+ 70
124+ ),
125+ message .replace (
126+ # Presence of \n breaks the Rich Text Format
127+ "\n " ,
128+ "<br>" ,
129+ ).format (
130+ product = "3ds Max" ,
131+ url_doc_supported_versions = '<a href="{u}">{u}</a>' .format (
132+ u = url_doc_supported_versions ,
133+ ),
134+ version = VERSION_OLDEST_COMPATIBLE ,
135+ ),
136+ )
137+ except : # nosec B110
138+ # It is unlikely that the above message will go through
139+ # on old versions of Max (Python2, Qt4, ...).
140+ # But there is nothing more we can do here.
141+ pass
142+
143+ raise sgtk .TankError (
144+ message .format (
145+ product = "3ds Max" ,
146+ url_doc_supported_versions = url_doc_supported_versions ,
147+ version = VERSION_OLDEST_COMPATIBLE ,
148+ )
149+ )
103150
104- highest_supported_version = self ._max_version_to_year (
105- MaxEngine .MAXIMUM_SUPPORTED_VERSION
151+ elif self .max_version_year < VERSION_OLDEST_SUPPORTED :
152+ # Older than the oldest supported version
153+ self .logger .warning (
154+ "Flow Production Tracking no longer supports {product} "
155+ "versions older than {version}" .format (
156+ product = "3ds Max" ,
157+ version = VERSION_OLDEST_SUPPORTED ,
158+ )
106159 )
107160
108- msg = (
109- "Flow Production Tracking!\n \n "
110- "The Flow Production Tracking has not yet been fully tested with 3ds Max versions greater than %s. "
111- "You can continue to use the Toolkit but you may experience bugs or instability. "
112- "Please report any issues you see via %s."
113- % (
114- highest_supported_version ,
115- sgtk .support_url ,
161+ if self .has_ui :
162+ QtGui .QMessageBox .warning (
163+ None , # parent
164+ "Warning - Flow Production Tracking Compatibility!" .ljust (
165+ # Padding to try to prevent the dialog being insanely narrow
166+ 70
167+ ),
168+ """
169+ Flow Production Tracking no longer supports {product} versions older than
170+ {version}.
171+ You can continue to use Toolkit but you may experience bugs or instabilities.
172+
173+ For information regarding support engine versions, please visit this page:
174+ {url_doc_supported_versions}
175+ """ .strip ()
176+ .replace (
177+ # Presence of \n breaks the Rich Text Format
178+ "\n " ,
179+ "<br>" ,
180+ )
181+ .format (
182+ product = "3ds Max" ,
183+ url_doc_supported_versions = '<a href="{u}">{u}</a>' .format (
184+ u = url_doc_supported_versions ,
185+ ),
186+ version = VERSION_OLDEST_SUPPORTED ,
187+ ),
188+ )
189+
190+ elif self .max_version_year <= VERSION_NEWEST_SUPPORTED :
191+ # Within the range of supported versions
192+ self .logger .debug (f"Running 3ds Max version { self .max_version_year } " )
193+ else : # Newer than the newest supported version (untested)
194+ self .logger .warning (
195+ "Flow Production Tracking has not yet been fully tested with "
196+ "{product} version {version}." .format (
197+ product = "3ds Max" ,
198+ version = self .max_version_year ,
116199 )
117200 )
118201
119- # Display warning dialog
120- max_year = self ._max_version_to_year (self ._get_max_version ())
121- max_next_year = highest_supported_version + 1
122- if max_year >= self .get_setting (
123- "compatibility_dialog_min_version" , max_next_year
202+ if self .has_ui and self .max_version_year >= self .get_setting (
203+ "compatibility_dialog_min_version"
124204 ):
125205 QtGui .QMessageBox .warning (
126- None , "PTR Warning" , "Warning - {0}" .format (msg )
206+ None , # parent
207+ "Warning - Flow Production Tracking Compatibility!" .ljust (
208+ # Padding to try to prevent the dialog being insanely narrow
209+ 70
210+ ),
211+ """
212+ Flow Production Tracking has not yet been fully tested with {product} version
213+ {version}.
214+ You can continue to use Toolkit but you may experience bugs or instabilities.
215+
216+ Please report any issues to:
217+ {support_url}
218+ """ .strip ()
219+ .replace (
220+ # Presence of \n breaks the Rich Text Format
221+ "\n " ,
222+ "<br>" ,
223+ )
224+ .format (
225+ product = "3ds Max" ,
226+ support_url = '<a href="{u}">{u}</a>' .format (
227+ u = sgtk .support_url ,
228+ ),
229+ version = self .max_version_year ,
230+ ),
127231 )
128- # and log the warning
129- self .log_warning (msg )
130232
131233 self ._safe_dialog = []
132234 # Keep the dialog to prevent the garbage collector from delete it
@@ -165,7 +267,7 @@ def eventFilter(self, obj, event):
165267 # possible that we'll get back a QCoreApplication from Max, which won't
166268 # carry references to a stylesheet. In that case, we apply our styling
167269 # to the dialog parent, which will be the top-level Max window.
168- if self ._max_version_to_year ( self . _get_max_version ()) < 2018 :
270+ if self .max_version_year < 2018 :
169271 parent_widget = sgtk .platform .qt .QtCore .QCoreApplication .instance ()
170272 else :
171273 parent_widget = self ._get_dialog_parent ()
@@ -175,7 +277,7 @@ def eventFilter(self, obj, event):
175277 if "toolkit 3dsmax style extension" not in curr_stylesheet :
176278 # If we're in pre-2017 Max then we need to handle our own styling. Otherwise
177279 # we just inherit from Max.
178- if self ._max_version_to_year ( self . _get_max_version ()) < 2017 :
280+ if self .max_version_year < 2017 :
179281 self ._initialize_dark_look_and_feel ()
180282
181283 curr_stylesheet += "\n \n /* toolkit 3dsmax style extension */ \n \n "
@@ -239,7 +341,7 @@ def _post_app_init(self):
239341 Called from the main thread when all apps have initialized
240342 """
241343 # set up menu handler
242- if self ._max_version_to_year ( self . _get_max_version ()) >= 2025 :
344+ if self .max_version_year >= 2025 :
243345 self ._menu_generator = self .tk_3dsmax .MenuGenerator_callbacks (self )
244346 self ._add_shotgun_menu ()
245347
@@ -361,8 +463,7 @@ def destroy_engine(self):
361463 Called when the engine is shutting down
362464 """
363465 self .log_debug ("%s: Destroying..." % self )
364-
365- if self ._max_version_to_year (self ._get_max_version ()) < 2025 :
466+ if self .max_version_year < 2025 :
366467 pymxs .runtime .callbacks .removeScripts (
367468 pymxs .runtime .Name ("postLoadingMenus" ),
368469 id = pymxs .runtime .Name ("sg_tk_on_menus_loaded" ),
@@ -410,7 +511,7 @@ def _get_dialog_parent(self):
410511 # Older versions of Max make use of special logic in _create_dialog
411512 # to handle window parenting. If we can, though, we should go with
412513 # the more standard approach to getting the main window.
413- if self ._max_version_to_year ( self . _get_max_version ()) > 2020 :
514+ if self .max_version_year > 2020 :
414515 # getMAXHWND returned a float instead of a long, which was completely
415516 # unusable with PySide in 2017 to 2020, but starting 2021
416517 # we can start using it properly.
@@ -442,7 +543,7 @@ def show_panel(self, panel_id, title, bundle, widget_class, *args, **kwargs):
442543
443544 self .log_debug ("Begin showing panel %s" % panel_id )
444545
445- if self ._max_version_to_year ( self . _get_max_version ()) <= 2017 :
546+ if self .max_version_year <= 2017 :
446547 # Qt docking is supported in version 2018 and later.
447548 self .log_warning (
448549 "Panel functionality not implemented. Falling back to showing "
@@ -657,17 +758,22 @@ def safe_dialog_exec(self, func):
657758 dialog .activateWindow () # for Windows
658759 dialog .raise_ () # for MacOS
659760
660- ##########################################################################################
661- # Latest supported max version
662- MAXIMUM_SUPPORTED_VERSION = 28000
663-
664- def _max_version_to_year (self , version ):
761+ @property
762+ def max_version_year (self ):
665763 """
666764 Get the max year from the max release version.
667- Note that while 17000 is 2015, 17900 would be 2016 alpha
668765 """
669- year = 2000 + (math .ceil (version / 1000.0 ) - 2 )
670- return year
766+
767+ if self ._max_version_year is None :
768+ max_version = self ._get_max_version ()
769+ try :
770+ assert isinstance (max_version [7 ], int )
771+ self ._max_version_year = max_version [7 ]
772+ except (AssertionError , IndexError , TypeError ) as err :
773+ self .log_debug ("Unable to extract Max version {}" .format (err ))
774+ self ._max_version_year = 0
775+
776+ return self ._max_version_year
671777
672778 def _get_max_version (self ):
673779 """
@@ -677,7 +783,7 @@ def _get_max_version(self):
677783 # Make sure this gets executed from the main thread because pymxs can't be used
678784 # from a background thread.
679785 self ._max_version = self .execute_in_main_thread (
680- lambda : pymxs .runtime .maxVersion ()[ 0 ]
786+ lambda : pymxs .runtime .maxVersion ()
681787 )
682788 # 3dsMax Version returns a number which contains max version, sdk version, etc...
683789 return self ._max_version
0 commit comments