Skip to content
12 changes: 6 additions & 6 deletions docs/platform.rst
Original file line number Diff line number Diff line change
Expand Up @@ -892,14 +892,14 @@ you from managing this by yourself, but for maximum compatibility and portabilty
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
from sgtk.platform.qt import QtCore, QtWidgets
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
from sgtk.platform.qt import QtCore, QtWidgets
from sgtk.platform.qt6 import QtWidgets


# derive from QtGui.QWidget for your UI components.
# derive from QtWidgets.QWidget for your UI components.

class AppDialog(QtGui.QWidget):
class AppDialog(QtWidgets.QWidget):

def __init__(self, param1, param2):
QtGui.QWidget.__init__(self)
QtWidgets.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.
Expand All @@ -924,12 +924,12 @@ property called ``exit_code``. Typically, your code for a modal dialog would loo

def on_ok_button_clicked(self):
# user clicked ok
self.exit_code = QtGui.QDialog.Accepted
self.exit_code = QtWidgets.QDialog.Accepted
self.close()

def on_cancel_button_clicked(self):
# user clicked cancel
self.exit_code = QtGui.QDialog.Rejected
self.exit_code = QtWidgets.QDialog.Rejected
self.close()

The call to self.engine.show_modal() will return the appropriate status code depending on which button was clicked.
Expand Down
6 changes: 3 additions & 3 deletions python/tank/authentication/interactive_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
# in the context of a DCC, but occur too early for the Toolkit logging to be
# fully in place to record it.
try:
from .ui.qt_abstraction import QtGui
from .ui.qt_abstraction import QtWidgets
except Exception:
QtGui = None
QtWidgets = None

logger = LogManager.get_logger(__name__)

Expand Down Expand Up @@ -77,7 +77,7 @@ def _get_ui_state():
Returns the state of UI: do we have a ui or not.
:returns: True or False)
"""
if QtGui and QtGui.QApplication.instance() is not None:
if QtWidgets and QtWidgets.QApplication.instance() is not None:
return True
else:
return False
Expand Down
8 changes: 4 additions & 4 deletions python/tank/authentication/invoker.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
# in the context of a DCC, but occur too early for the Toolkit logging to be
# fully in place to record it.
try:
from .ui.qt_abstraction import QtCore, QtGui
from .ui.qt_abstraction import QtCore, QtWidgets
except Exception:
QtCore, QtGui = None, None
QtCore, QtWidgets = None, None


def create():
Expand Down Expand Up @@ -59,7 +59,7 @@ def show_ui():
thread will be produced.
"""
# If we are already in the main thread, no need for an invoker, invoke directly in this thread.
if QtCore.QThread.currentThread() == QtGui.QApplication.instance().thread():
if QtCore.QThread.currentThread() == QtWidgets.QApplication.instance().thread():
return lambda fn, *args, **kwargs: fn(*args, **kwargs)

class MainThreadInvoker(QtCore.QObject):
Expand All @@ -80,7 +80,7 @@ def __init__(self):
self._res = None
self._exception = None
# Make sure that the invoker is bound to the main thread
self.moveToThread(QtGui.QApplication.instance().thread())
self.moveToThread(QtWidgets.QApplication.instance().thread())

def __call__(self, fn, *args, **kwargs):
"""
Expand Down
40 changes: 21 additions & 19 deletions python/tank/authentication/login_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
QtGui,
QtCore,
QtNetwork,
QtWebKit,
QtWebEngineWidgets,
QtWidgets,
QtWebEngineCore,
Comment on lines 39 to +41
Copy link
Contributor

Choose a reason for hiding this comment

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

Please re-alpha order

qt_version_tuple,
)
from . import app_session_launcher
Expand Down Expand Up @@ -115,7 +116,7 @@ def run(self):
"""
self._site_info.reload(self._url_to_test, self._http_proxy)

class LoginDialog(QtGui.QDialog):
class LoginDialog(QtWidgets.QDialog):
"""
Dialog for getting user credentials.
"""
Expand Down Expand Up @@ -145,14 +146,15 @@ def __init__(
:param parent: The Qt parent for the dialog (defaults to None)
:param session_metadata: Metadata used in the context of SSO. This is an obscure blob of data.
"""
QtGui.QDialog.__init__(self, parent)
QtWidgets.QDialog.__init__(self, parent)
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason to not use super()?


qt_modules = {
"QtCore": QtCore,
"QtGui": QtGui,
"QtNetwork": QtNetwork,
"QtWebKit": QtWebKit,
"QtWidgets": QtWidgets,
"QtWebEngineWidgets": QtWebEngineWidgets,
"QtWebEngineCore": QtWebEngineCore,
}
try:
self._sso_saml2 = SsoSaml2Toolkit(
Expand Down Expand Up @@ -247,7 +249,7 @@ def __init__(
self.ui.stackedWidget.setCurrentWidget(self.ui.login_page)

# Initialize Options menu
menu = QtGui.QMenu(self.ui.button_options)
menu = QtWidgets.QMenu(self.ui.button_options)
self.ui.button_options.setMenu(menu)
self.ui.button_options.setVisible(False)

Expand Down Expand Up @@ -323,11 +325,11 @@ def __init__(
)

# Initialize exit confirm message box
self.confirm_box = QtGui.QMessageBox(
QtGui.QMessageBox.Question,
self.confirm_box = QtWidgets.QMessageBox(
QtWidgets.QMessageBox.Question,
"Flow Production Tracking Login", # title
"Would you like to cancel your request?", # text
buttons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
buttons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
# parent=self,
# Passing the parent parameter here, in the constructor, makes
# Nuke versions<=13 crash.
Expand Down Expand Up @@ -355,7 +357,7 @@ def __del__(self):
self._query_task.wait()

def _confirm_exit(self):
return self.confirm_box.exec_() == QtGui.QMessageBox.StandardButton.Yes
return self.confirm_box.exec_() == QtWidgets.QMessageBox.StandardButton.Yes
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return self.confirm_box.exec_() == QtWidgets.QMessageBox.StandardButton.Yes
return self.confirm_box.exec() == QtWidgets.QMessageBox.StandardButton.Yes

# PySide uses "exec_" instead of "exec" because "exec" is a reserved
# keyword in Python 2.

Expand Down Expand Up @@ -666,7 +668,7 @@ def exec_(self):
# to freeze, so only set the WindowStaysOnTopHint flag as this appears to not disable the
# other flags.
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
return QtGui.QDialog.exec_(self)
return QtWidgets.QDialog.exec_(self)
Copy link
Contributor

Choose a reason for hiding this comment

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

What about the exec_ method? I though we were not supposed to use it anymore with recent syntax?

Copy link
Contributor

Choose a reason for hiding this comment

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

According to Copilot, on this same line, asking if the exec_ method is deprecated:

No, the exec_ method is not deprecated in PySide6, but the preferred method name is now exec. In PySide6 (and PyQt6), exec_() is still available for backward compatibility, but the trailing underscore was originally used because exec is a reserved keyword in Python 2. Since Python 3, exec is no longer a reserved word, so you can use exec() directly.

So, for PySide6, you should use:

return QtWidgets.QDialog.exec(self)

The exec_() method will still work in PySide6 for now, but using exec() is the modern and recommended approach. If you want your code to be future-proof and style-compliant with the latest PySide6 standards, switch to exec().

Summary:

  • exec_() is not deprecated yet, but is considered legacy.
  • exec() is preferred in PySide6 and PyQt6.

However, reading the documentation and not relying only on AI (I should've done this in the first place to avoid double lookup), I realized that we're encouraged to use open instead (Reference).

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the detailed explanation @carlos-villavicencio-adsk!

However, switching from exec_ to open is not straightforward because I am aware that, in some places (TK auth), we rely on exec_ blocking call. So we would have to re-think the workflow to use open.


def result(self):
"""
Expand All @@ -687,13 +689,13 @@ def result(self):
profile_location=profile_location,
)
# If the offscreen session renewal failed, show the GUI as a failsafe
if res != QtGui.QDialog.Accepted:
if res != QtWidgets.QDialog.Accepted:
return

return self._sso_saml2.get_session_data()

res = self.exec_()
if res != QtGui.QDialog.Accepted:
if res != QtWidgets.QDialog.Accepted:
return

metrics_cache.log(
Expand Down Expand Up @@ -746,15 +748,15 @@ def _ok_pressed(self):
Validate the values, accepting if login is successful and display an error message if not.
"""
# Wait for any ongoing Site Configuration check thread.
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
try:
if not self._query_task.wait(THREAD_WAIT_TIMEOUT_MS):
logger.warning(
"Timed out awaiting configuration information on the site: %s"
% self._get_current_site()
)
finally:
QtGui.QApplication.restoreOverrideCursor()
QtWidgets.QApplication.restoreOverrideCursor()

# pull values from the gui
site = self._get_current_site()
Expand Down Expand Up @@ -826,7 +828,7 @@ def _authenticate(self, error_label, site, login, password, auth_code=None):
product=PRODUCT_IDENTIFIER,
profile_location=profile_location,
)
if res == QtGui.QDialog.Accepted:
if res == QtWidgets.QDialog.Accepted:
self._new_session_token = self._sso_saml2.session_id
self._session_metadata = self._sso_saml2.cookies
else:
Expand All @@ -836,8 +838,8 @@ def _authenticate(self, error_label, site, login, password, auth_code=None):
return
else:
# set the wait cursor
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
QtGui.QApplication.processEvents()
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
QtWidgets.QApplication.processEvents()

# try and authenticate
self._new_session_token = session_cache.generate_session_token(
Expand All @@ -850,9 +852,9 @@ def _authenticate(self, error_label, site, login, password, auth_code=None):
success = True
finally:
# restore the cursor
QtGui.QApplication.restoreOverrideCursor()
QtWidgets.QApplication.restoreOverrideCursor()
# dialog is done
QtGui.QApplication.processEvents()
QtWidgets.QApplication.processEvents()

# Do not accept while the cursor is overriden, if freezes the dialog.
if success:
Expand Down
2 changes: 1 addition & 1 deletion python/tank/authentication/sso_saml2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
SsoSaml2MissingQtGui,
SsoSaml2MissingQtModuleError,
SsoSaml2MissingQtNetwork,
SsoSaml2MissingQtWebKit,
SsoSaml2MissingQtWebEngineWidgets,
SsoSaml2MultiSessionNotSupportedError,
)

Expand Down
4 changes: 2 additions & 2 deletions python/tank/authentication/sso_saml2/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class SsoSaml2MissingQtNetwork(SsoSaml2MissingQtModuleError):
"""


class SsoSaml2MissingQtWebKit(SsoSaml2MissingQtModuleError):
class SsoSaml2MissingQtWebEngineWidgets(SsoSaml2MissingQtModuleError):
"""
Exception that indicates that the QtWebKit component is missing.
Exception that indicates that the QtWebEngineWidgets component is missing.
"""


Expand Down
39 changes: 21 additions & 18 deletions python/tank/authentication/sso_saml2/core/sso_saml2_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
SsoSaml2MissingQtCore,
SsoSaml2MissingQtGui,
SsoSaml2MissingQtNetwork,
SsoSaml2MissingQtWebKit,
SsoSaml2MissingQtWebEngineWidgets,
)
from .utils import (
_decode_cookies,
Expand Down Expand Up @@ -154,7 +154,7 @@ def __init__(self, window_title="Web Login", qt_modules=None):

:param window_title: Title to use for the window.
:param qt_modules: a dictionnary of required Qt modules.
For Qt4/PySide, we require modules QtCore, QtGui, QtNetwork and QtWebKit
For Qt4/PySide, we require modules QtCore, QtGui, QtNetwork and QtWebEngineWidgets

:returns: The SsoSaml2Core oject.
"""
Expand All @@ -170,10 +170,13 @@ def __init__(self, window_title="Web Login", qt_modules=None):
QtCore = self._QtCore = qt_modules.get("QtCore") # noqa
QtGui = self._QtGui = qt_modules.get("QtGui") # noqa
QtNetwork = self._QtNetwork = qt_modules.get("QtNetwork") # noqa
QtWebKit = self._QtWebKit = qt_modules.get("QtWebKit") # noqa
QtWidgets = self._QtWidgets = qt_modules.get("QtWidgets") # noqa
QtWebEngineWidgets = self._QtWebEngineWidgets = qt_modules.get(
"QtWebEngineWidgets"
) # noqa
QtWebEngineCore = self._QtWebEngineCore = qt_modules.get(
"QtWebEngineCore"
) # noqa

if QtCore is None:
raise SsoSaml2MissingQtCore("The QtCore module is unavailable")
Expand All @@ -184,9 +187,9 @@ def __init__(self, window_title="Web Login", qt_modules=None):
if QtNetwork is None:
raise SsoSaml2MissingQtNetwork("The QtNetwork module is unavailable")

if QtWebKit is None and QtWebEngineWidgets is None:
raise SsoSaml2MissingQtWebKit(
"The QtWebKit or QtWebEngineWidgets modules are unavailable"
if QtWebEngineWidgets is None:
raise SsoSaml2MissingQtWebEngineWidgets(
"The QtWebEngineWidgets modules are unavailable"
)

# If PySide2 is being used, we need to make extra checks to ensure
Expand All @@ -203,15 +206,15 @@ def __init__(self, window_title="Web Login", qt_modules=None):
# - Maya 2017
# missing the 'QSslConfiguration' class. Likely compiled without SSL
# support.
if QtWebEngineWidgets and not hasattr(
QtWebEngineWidgets.QWebEngineProfile, "cookieStore"
if QtWebEngineCore and not hasattr(
QtWebEngineCore.QWebEngineProfile, "cookieStore"
):
raise SsoSaml2IncompletePySide2(
"Missing method QtWebEngineWidgets.QWebEngineProfile.cookieStore()"
)
if QtNetwork and not hasattr(QtNetwork, "QSslConfiguration"):
raise SsoSaml2IncompletePySide2("Missing class QtNetwork.QSslConfiguration")
class TKWebPageQtWebEngine(QtWebEngineWidgets.QWebEnginePage):
class TKWebPageQtWebEngine(QtWebEngineCore.QWebEnginePage):
"""
Wrapper class to better control the behaviour when clicking on links
in the Qt5 web browser. If we are asked to open a new tab/window, then
Expand Down Expand Up @@ -251,7 +254,7 @@ def acceptNavigationRequest(self, url, n_type, is_mainframe):
if self._profile is None:
QtGui.QDesktopServices.openUrl(url)
return False
return QtWebEngineWidgets.QWebEnginePage.acceptNavigationRequest(
return QtWebEngineCore.QWebEnginePage.acceptNavigationRequest(
self, url, n_type, is_mainframe
)

Expand Down Expand Up @@ -279,17 +282,17 @@ def certificateError(self, certificate_error):
self._sessions_stack = []
self._session_renewal_active = False

self._dialog = QtGui.QDialog()
self._dialog = QtWidgets.QDialog()
self._dialog.setWindowTitle(window_title)
self._dialog.finished.connect(self.on_dialog_closed)

# This is to ensure that we can resize the window nicely, and that the
# WebView will follow.
self._layout = QtGui.QVBoxLayout(self._dialog)
self._layout = QtWidgets.QVBoxLayout(self._dialog)
self._layout.setSpacing(0)
self._layout.setContentsMargins(0, 0, 0, 0)

self._profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
self._profile = QtWebEngineCore.QWebEngineProfile.defaultProfile()
self._logger.debug(
"Initial WebEngineProfile storage location: %s",
self._profile.persistentStoragePath(),
Expand All @@ -313,7 +316,7 @@ def certificateError(self, certificate_error):
# The cookies will be cleared if there are no prior session in
# method 'update_browser_from_session' if needed.
self._profile.setPersistentCookiesPolicy(
QtWebEngineWidgets.QWebEngineProfile.ForcePersistentCookies
QtWebEngineCore.QWebEngineProfile.ForcePersistentCookies
)
self._cookie_jar = QtNetwork.QNetworkCookieJar()
self._profile.cookieStore().cookieAdded.connect(self._on_cookie_added)
Expand Down Expand Up @@ -860,14 +863,14 @@ def on_dialog_closed(self, result):
This can be the result of a callback, a timeout or user interaction.

:param result: Qt result following the closing of the dialog.
QtGui.QDialog.Accepted or QtGui.QDialog.Rejected
QtWidgets.QDialog.Accepted or QtGui.QDialog.Rejected
"""
self._logger.debug("SSO dialog closed")
# pylint: disable=invalid-name
QtGui = self._QtGui # noqa
QtWidgets = self._QtWidgets # noqa

if self.is_handling_event():
if result == QtGui.QDialog.Rejected and self._session.cookies != "":
if result == QtWidgets.QDialog.Rejected and self._session.cookies != "":
# We got here because of a timeout attempting a GUI-less login.
# Let's clear the cookies, and force the use of the GUI.
self._session.cookies = ""
Expand All @@ -882,7 +885,7 @@ def on_dialog_closed(self, result):
self.resolve_event()
else:
# Should we get a rejected dialog, then we have had a timeout.
if result == QtGui.QDialog.Rejected:
if result == QtWidgets.QDialog.Rejected:
# @FIXME: Figure out exactly what to do when we have a timeout.
self._logger.warn(
"Our QDialog got canceled outside of an event handling"
Expand Down
Loading