Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SG-30560 Code Cleanup: Removing Qt4/PySide/PyQt4 compatibility in Toolkit #973

Merged
merged 13 commits into from
Jun 20, 2024
Merged
11 changes: 0 additions & 11 deletions python/tank/authentication/login_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,17 +419,6 @@ def _on_site_changed(self):
Called when the user is done editing the site. It will refresh the
list of recent users.
"""

# When running with PySide/Qt4, the editingFinished event is trigger
# even if the text field has not changed.
# This is not the case in PySide2/Qt5 or PySide6/Qt6.
if (
qt_version_tuple[0] == 4
and self._get_current_site() == self.host_selected
):
logger.debug("_on_site_changed - host has not changed")
return

self.ui.login.clear()
self._populate_user_dropdown(self._get_current_site())
self._update_ui_according_to_site_support()
Expand Down
337 changes: 103 additions & 234 deletions python/tank/authentication/sso_saml2/core/sso_saml2_core.py
eduardoChaucaGallegos marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

81 changes: 2 additions & 79 deletions python/tank/authentication/ui/qt5_like_line_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,84 +13,7 @@
"""


from .qt_abstraction import QtGui, QtCore, qt_version_tuple
from .qt_abstraction import QtGui


# If we're on Qt4, we want the placeholder in the login dialog.
if qt_version_tuple[0] == 4:
class Qt5LikeLineEdit(QtGui.QLineEdit):
"""
QLineEdit that shows a placeholder in an empty editor, even if the widget is focused.
"""

# Constants taken from
# http://code.metager.de/source/xref/lib/qt/src/gui/widgets/qlineedit_p.cpp#59
_horizontal_margin = 2
_vertical_margin = 1

def paintEvent(self, paint_event):
"""
Paints the line editor and adds a placeholder on top when the string is empty, even if focused.
"""
# Draws the widget. If the widget is out of focus, it will draw the placeholder.
QtGui.QLineEdit.paintEvent(self, paint_event)

# This code is based on the C++ implementation of the paint event.
# http://code.metager.de/source/xref/lib/qt/src/gui/widgets/qlineedit.cpp#1889
#
# The translated code keeps the flow and the naming as close as possible to the original
# in order to easily map one section and variable to another, unless preserving such
# consistency actually made the code harder to read.

# If the box is empty and focused, draw the placeholder
if self.hasFocus() and not self.text() and self.placeholderText():

p = QtGui.QPainter(self)
pal = self.palette()

panel = QtGui.QStyleOptionFrameV2()
self.initStyleOption(panel)
r = self.style().subElementRect(QtGui.QStyle.SE_LineEditContents, panel, self)

text_margins = self.textMargins()

# self.textMargins() retrieves the same values as d->rightTextMargin, ... in the C++ code.
r.setX(r.x() + text_margins.left())
r.setY(r.y() + text_margins.top())
r.setRight(r.right() - text_margins.right())
r.setBottom(r.bottom() - text_margins.bottom())
p.setClipRect(r)

fm = self.fontMetrics()

visual_alignment = QtGui.QStyle.visualAlignment(self.layoutDirection(), QtCore.Qt.AlignLeft)
vertical_alignment = visual_alignment & QtCore.Qt.AlignVertical_Mask

if vertical_alignment == QtCore.Qt.AlignBottom:
vscroll = r.y() + r.height() - fm.height() - self._vertical_margin
elif vertical_alignment == QtCore.Qt.AlignTop:
vscroll = r.y() + self._vertical_margin
else:
vscroll = r.y() + (r.height() - fm.height() + 1) / 2

line_rect = QtCore.QRect(
r.x() + self._horizontal_margin,
vscroll,
r.width() - 2 * self._horizontal_margin,
fm.height()
)

min_left_bearing = max(0, -fm.minLeftBearing())

col = pal.text().color()
col.setAlpha(128)
oldpen = p.pen()
p.setPen(col)
line_rect.adjust(min_left_bearing, 0, 0, 0)

elided_text = fm.elidedText(self.placeholderText(), QtCore.Qt.ElideRight, line_rect.width())
p.drawText(line_rect, vertical_alignment, elided_text)
p.setPen(oldpen)
else:
# Qt5 always has the placeholder.
Qt5LikeLineEdit = QtGui.QLineEdit
Qt5LikeLineEdit = QtGui.QLineEdit
julien-lang marked this conversation as resolved.
Show resolved Hide resolved
104 changes: 1 addition & 103 deletions python/tank/platform/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,17 +642,6 @@ def has_qt6(self):
"""
return self.__has_qt6

@property
def has_qt4(self):
"""
Indicates that the host application has access to Qt 4 and that the ``sgtk.platform.qt`` module
has been populated with the Qt 4 modules and information.

:returns bool: boolean value indicating if Qt 4 is available.
"""
# Check if Qt was imported. Then checks if a Qt4 compatible api is available.
return hasattr(qt, "QtGui") and hasattr(qt.QtGui, "QApplication")

@property
def metrics_dispatch_allowed(self):
"""
Expand Down Expand Up @@ -2215,7 +2204,6 @@ def _initialize_dark_look_and_feel(self):
(for Qt5), and set it up with a standard dark palette and supporting
stylesheet.

`Qt4 setStyle documentation <http://doc.qt.io/archives/qt-4.8/qapplication.html#setStyle-2>`_
`Qt5 setStyle documentation <https://doc.qt.io/qt-5.10/qapplication.html#setStyle-1>`_

Apps and UIs can then extend this further by using further css.
Expand All @@ -2226,12 +2214,9 @@ def _initialize_dark_look_and_feel(self):
if self.has_qt5:
self.log_debug("Applying Qt5-specific styling...")
self.__initialize_dark_look_and_feel_qt5()
elif self.has_qt4:
carlos-villavicencio-adsk marked this conversation as resolved.
Show resolved Hide resolved
self.log_debug("Applying Qt4-specific styling...")
self.__initialize_dark_look_and_feel_qt4()
else:
self.log_warning(
"Neither Qt4 or Qt5 is available. Toolkit styling will not be applied."
"Qt5 is not available. Toolkit styling will not be applied."
eduardoChaucaGallegos marked this conversation as resolved.
Show resolved Hide resolved
)

def __initialize_dark_look_and_feel_qt5(self):
Expand Down Expand Up @@ -2414,93 +2399,6 @@ def __initialize_dark_look_and_feel_qt5(self):
# used with the fusion style.
app.setStyleSheet(".QWidget { font-size: 11px; }")

def __initialize_dark_look_and_feel_qt4(self):
"""
Applies a dark style for Qt4 environments. This sets the "plastique"
style at the application level, and then loads a Maya-2014-like QPalette
to give a consistent dark theme to all widgets owned by the current
application. Lastly, a stylesheet is read from disk and applied.
"""
from .qt import QtGui, QtCore

# Since know we have a QApplication at this point, go ahead and make
# sure the bundled fonts are loaded
self._ensure_core_fonts_loaded()

# initialize our style
QtGui.QApplication.setStyle("plastique")

# Read in a serialized version of a palette
# this file was generated in the following way:
#
# Inside of maya 2014, the following code was executed:
#
# from PySide import QtGui, QtCore
# app = QtCore.QCoreApplication.instance()
# fh = QtCore.QFile("/tmp/palette.dump")
# fh.open(QtCore.QIODevice.WriteOnly)
# out = QtCore.QDataStream(fh)
# out.__lshift__( app.palette() )
# fh.close()
#
# When we load this up in our engine, we will get a look
# and feel similar to that of maya.

try:
# open palette file
palette_file = self.__get_platform_resource_path("dark_palette.qpalette")
fh = QtCore.QFile(palette_file)
fh.open(QtCore.QIODevice.ReadOnly)
file_in = QtCore.QDataStream(fh)

# deserialize the palette
# (store it for GC purposes)
self._dark_palette = QtGui.QPalette()
file_in.__rshift__(self._dark_palette)
fh.close()

# set the std selection bg color to be 'PTR blue'
highlight_color = QtGui.QBrush(
QtGui.QColor(constants.SG_STYLESHEET_CONSTANTS["SG_HIGHLIGHT_COLOR"])
)
self._dark_palette.setBrush(QtGui.QPalette.Highlight, highlight_color)

# update link colors
fg_color = self._dark_palette.color(QtGui.QPalette.Text)
self._dark_palette.setColor(QtGui.QPalette.Link, fg_color)
self._dark_palette.setColor(QtGui.QPalette.LinkVisited, fg_color)

self._dark_palette.setBrush(
QtGui.QPalette.HighlightedText, QtGui.QBrush(QtGui.QColor("#FFFFFF"))
)

# and associate it with the qapplication
QtGui.QApplication.setPalette(self._dark_palette)

except Exception as e:
self.log_error(
"The standard toolkit dark palette could not be set up! The look and feel of your "
"toolkit apps may be sub standard. Please contact support. Details: %s"
% e
)

try:
# read css
css_file = self.__get_platform_resource_path("dark_palette.css")
f = open(css_file)
css_data = f.read()
f.close()
css_data = self._resolve_sg_stylesheet_tokens(css_data)
app = QtCore.QCoreApplication.instance()

app.setStyleSheet(css_data)
except Exception as e:
self.log_error(
"The standard toolkit dark stylesheet could not be set up! The look and feel of your "
"toolkit apps may be sub standard. Please contact support. Details: %s"
% e
)

def _get_standard_qt_stylesheet(self):
"""
**********************************************************************
Expand Down
63 changes: 1 addition & 62 deletions python/tank/util/qt_importer.py
carlos-villavicencio-adsk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ def _import_module_by_name(self, parent_module_name, module_name):
logger.debug("Unable to import module '%s': %s", module_name, e)
pass
return module

def _import_pyside(self):
eduardoChaucaGallegos marked this conversation as resolved.
Show resolved Hide resolved
"""
Imports PySide.

Expand Down Expand Up @@ -318,43 +316,6 @@ def _import_pyside2_as_pyside(self):
self._to_version_tuple(QtCore.qVersion()),
)

def _import_pyqt4(self):
"""
Imports PyQt4.

:returns: The (binding name, binding version, modules) tuple.
"""
from PyQt4 import QtCore, QtGui, Qt

# hot patch the library to make it compatible with PySide-based apps.
QtCore.Signal = QtCore.pyqtSignal
QtCore.Slot = QtCore.pyqtSlot
QtCore.Property = QtCore.pyqtProperty

QtNetwork = self._import_module_by_name("PyQt4", "QtNetwork")
QtWebKit = self._import_module_by_name("PyQt4", "QtWebKit")

# Note: Do not remove this. It was briefly introduced so that engines
# could introspec the wrapper for all sorts of things, but we've moving
# away from modifying the modules themselves, so keep this is for now and
# we'll be able to deprecate it at some point in the future.
import PyQt4

PyQt4.__version__ = Qt.PYQT_VERSION_STR

return (
"PyQt4",
PyQt4.__version__,
PyQt4,
{
"QtCore": QtCore,
"QtGui": QtGui,
"QtNetwork": QtNetwork,
"QtWebKit": QtWebKit,
},
self._to_version_tuple(QtCore.QT_VERSION_STR),
)

def _import_pyside6_as_pyside(self): # pragma: no cover
"""
Import PySide6 and expose its modules through the Qt4 (PySide) interface.
Expand Down Expand Up @@ -432,8 +393,6 @@ def _import_modules(self, interface_version_requested):
"""
Tries to import different Qt binding implementation in the following order:
- PySide2
- PySide
- PyQt4
- PySide6

PySide6 is attempted to be imported last at the moment because it is is not yet fully
Expand All @@ -459,7 +418,7 @@ def _import_modules(self, interface_version_requested):
return pyside2
except ImportError:
pass
elif interface_version_requested == self.QT5:
carlos-villavicencio-adsk marked this conversation as resolved.
Show resolved Hide resolved
if interface_version_requested == self.QT5:
julien-lang marked this conversation as resolved.
Show resolved Hide resolved
try:
pyside2 = self._import_pyside2()
logger.debug("Imported PySide2.")
Expand All @@ -471,26 +430,6 @@ def _import_modules(self, interface_version_requested):
logger.debug("Qt6 interface not implemented for Qt5")
pass

# We do not test for PyQt5 since it is supported on Python 3 only at the moment.

# Now try PySide 1
if interface_version_requested == self.QT4:
try:
pyside = self._import_pyside()
logger.debug("Imported PySide1.")
return pyside
except ImportError:
pass

# Now try PyQt4
if interface_version_requested == self.QT4:
try:
pyqt = self._import_pyqt4()
logger.debug("Imported PyQt4.")
return pyqt
except ImportError:
pass

# Last attempt, try PySide6. PySide6 is not yet fully supported but allow DCCs that
# require PySide6 to run with the current support
if interface_version_requested == self.QT4:
Expand Down
25 changes: 0 additions & 25 deletions tests/python/tank_test/tank_test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"skip_if_pyside",
"skip_if_pyside2",
"skip_if_pyside6",
"skip_if_pyqt4",
]


Expand Down Expand Up @@ -214,30 +213,6 @@ def _skip_if_pyside2(func):

return _skip_if_pyside2

def _has_pyqt4():
"""
Tests if PyQt4 is avalable.
:returns: True if PyQt4 is available, False otherwise.
"""
pyqt4_spec = importlib.util.find_spec("PyQt4")
found = pyqt4_spec is not None
return found

def skip_if_pyqt4(found=True):
"""
Decorator that allows to skip tests based on if PyQt4 module found or not.
:param func: Function to be decorated.
:param found: True will skip if PyQt4 is found, else will skip if PyQt4 not found
(e.g. missing). Default to skip if PyQt4 found.
:returns: The decorated function.
"""

def _skip_if_pyqt4(func):
found_pyqt4= _has_pyqt4()
msg = "PyQt4 found" if found else "PyQt4 missing"
return unittest.skipIf(found_pyqt4 == found, msg)(func)

return _skip_if_pyqt4

def _has_pyside6():
"""
Expand Down
Loading
Loading