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

Gaffer : Support for Python 3.11 and Qt/PySide 6.5.3 #6216

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ else:
"/experimental:external", # Allow use of /external:I
"/external:W0", # Suppress warnings for headers included with /external:I
"/Zc:inline", # Remove unreferenced function or data if it is COMDAT or has internal linkage only
"/Zc:__cplusplus", # "Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."
"/GR", # Enable RTTI
"/TP", # Treat all files as c++ (vs C)
"/FC", # Display full paths in diagnostics
Expand Down
4 changes: 2 additions & 2 deletions include/GafferBindings/DependencyNodeBinding.inl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ DependencyNodeClass<T, Ptr>::DependencyNodeClass( const char *docString )
this->def( "enabledPlug", &Detail::enabledPlug<T> );
this->def( "correspondingInput", &Detail::correspondingInput<T> );
// Install our custom metaclass.
Py_TYPE( this->ptr() ) = Detail::dependencyNodeMetaclass();
Py_SET_TYPE( this->ptr(), Detail::dependencyNodeMetaclass() );
}

template<typename T, typename Ptr>
Expand All @@ -92,7 +92,7 @@ DependencyNodeClass<T, Ptr>::DependencyNodeClass( const char *docString, boost::
this->def( "enabledPlug", &Detail::enabledPlug<T> );
this->def( "correspondingInput", &Detail::correspondingInput<T> );
// Install our custom metaclass.
Py_TYPE( this->ptr() ) = Detail::dependencyNodeMetaclass();
Py_SET_TYPE( this->ptr(), Detail::dependencyNodeMetaclass() );
}

} // namespace GafferBindings
6 changes: 3 additions & 3 deletions python/GafferUI/CompoundEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,15 +1040,15 @@ def eventFilter( self, qObject, qEvent ) :
if qEventType not in self.__eventMask :
return False

if qEventType == qEvent.MouseButtonPress :
if qEventType == QtCore.QEvent.MouseButtonPress :

return self.__mousePress( qObject, qEvent )

elif qEventType == qEvent.MouseMove :
elif qEventType == QtCore.QEvent.MouseMove :

return self.__mouseMove( qObject, qEvent )

elif qEventType == qEvent.MouseButtonRelease :
elif qEventType == QtCore.QEvent.MouseButtonRelease :

return self.__mouseRelease( qObject, qEvent )

Expand Down
10 changes: 8 additions & 2 deletions python/GafferUI/EventLoop.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@

from Qt import QtCore
from Qt import QtWidgets
import Qt

## This class provides the event loops used to run GafferUI based applications.
class EventLoop( object ) :
Expand Down Expand Up @@ -106,7 +107,10 @@ def start( self ) :

if self.__runStyle == self.__RunStyle.Normal :
assert( self.__startCount == 1 )
self.__qtEventLoop.exec_()
if Qt.__binding__ == "PySide6" :
self.__qtEventLoop.exec()
else:
self.__qtEventLoop.exec_()
elif self.__runStyle == self.__RunStyle.PumpThread :
if self.__pumpThread is None :
self.__pumpThread = threading.Thread( target = self.__pumpThreadFn )
Expand Down Expand Up @@ -162,7 +166,9 @@ def running( self ) :
style = QtWidgets.QApplication.setStyle( "Fusion" )
# Stop icons/fonts being tiny on high-dpi monitors. Must be set before
# the application is created.
QtWidgets.QApplication.setAttribute( QtCore.Qt.AA_EnableHighDpiScaling )
# This attribute is deprecated in Qt6 and not needed.
if not Qt.__binding__ == "PySide6" :
QtWidgets.QApplication.setAttribute( QtCore.Qt.AA_EnableHighDpiScaling )
# Allow all `GafferUI.GLWidgets` to share OpenGL resources (via the underlying
# QOpenGLWidget).
QtWidgets.QApplication.setAttribute( QtCore.Qt.AA_ShareOpenGLContexts )
Expand Down
39 changes: 23 additions & 16 deletions python/GafferUI/GLWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,19 @@
import Qt
from Qt import QtCore

# Importing directly rather than via Qt.py because Qt.py won't expose the
# Qt-5-only QOpenGLWidget and QSurfaceFormat classes that we need. Their mantra
# is to provide only what is available in Qt4/PySide1 - see
# https://github.com/mottosso/Qt.py/issues/341.
## \todo Now that Qt 4 is long gone, and PySide is an official
# Qt project, Qt.py isn't much help. Remove across the board, or see
# if we can coax the project into bridging Qt 5/6 instead of 4/5?
from PySide2 import QtGui
from PySide2 import QtWidgets
from Qt import QtOpenGL
# Looks like Qt.py as of v1.4.1 doesn't expose any OpenGL, so use
# PySide2 or PySide6 directly and work around their differences manually.
if Qt.__binding__ == "PySide2" :
from PySide2 import QtGui
from PySide2 import QtWidgets
from PySide2.QtWidgets import QOpenGLWidget
from PySide2 import QtOpenGL
elif Qt.__binding__ == "PySide6" :
from PySide6 import QtGui
from PySide6 import QtWidgets
from PySide6.QtOpenGLWidgets import QOpenGLWidget
else :
raise Exception( "GafferUI : No compatible Python binding found for Qt" )

## The GLWidget is a base class for all widgets which wish to draw using OpenGL.
# Derived classes override the _draw() method to achieve this.
Expand Down Expand Up @@ -215,7 +218,7 @@ def __init__( self, format ) :

glWidget = self.__createGLWidget( format )
self.setViewport( glWidget )
self.setViewportUpdateMode( self.FullViewportUpdate )
self.setViewportUpdateMode( QtWidgets.QGraphicsView.FullViewportUpdate )

# QAbstractScrollArea (one of our base classes), implements
# minimumSizeHint() to include enough room for scrollbars.
Expand Down Expand Up @@ -262,7 +265,7 @@ def __createGLWidget( cls, format ) :
if result is not None :
return result

glWidget = QtWidgets.QOpenGLWidget()
glWidget = QOpenGLWidget()
# Avoid `QOpenGLFramebufferObject: Framebuffer incomplete attachment`
# errors caused by Qt trying to make a framebuffer with zero size.
glWidget.setMinimumSize( 1, 1 )
Expand Down Expand Up @@ -302,10 +305,14 @@ def __createHostedQGLWidget( cls, format ) :
# context should therefore be made current before calling this
# method.

qGLFormat = cls.__createQGLFormat( format )
result = QtOpenGL.QGLWidget()
_GafferUI._glWidgetSetHostedContext( GafferUI._qtAddress( result ), GafferUI._qtAddress( qGLFormat ) )
return result
try :
qGLFormat = cls.__createQGLFormat( format )
result = QtOpenGL.QGLWidget()
_GafferUI._glWidgetSetHostedContext( GafferUI._qtAddress( result ), GafferUI._qtAddress( qGLFormat ) )
return result
except :
result = QOpenGLWidget()
return result

@classmethod
def __createMayaQGLWidget( cls, format ) :
Expand Down
4 changes: 2 additions & 2 deletions python/GafferUI/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def _qtPixmapHighlighted( self ) :
effect.setColor( QtGui.QColor( 119, 156, 189, 255 ) )
effect.setStrength( 0.85 )
pixmapItem.setGraphicsEffect( effect )
pixmapItem.setShapeMode( pixmapItem.BoundingRectShape )
pixmapItem.setShapeMode( QtWidgets.QGraphicsPixmapItem.BoundingRectShape )

graphicsView = QtWidgets.QGraphicsView()
graphicsView.setScene( graphicsScene )
Expand Down Expand Up @@ -183,7 +183,7 @@ def _qtPixmapDisabled( self ) :
effect = QtWidgets.QGraphicsOpacityEffect()
effect.setOpacity( 0.4 )
pixmapItem.setGraphicsEffect( effect )
pixmapItem.setShapeMode( pixmapItem.BoundingRectShape )
pixmapItem.setShapeMode( QtWidgets.QGraphicsPixmapItem.BoundingRectShape )

graphicsView = QtWidgets.QGraphicsView()
graphicsView.setScene( graphicsScene )
Expand Down
10 changes: 7 additions & 3 deletions python/GafferUI/MenuBar.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from Qt import QtCore
from Qt import QtGui
from Qt import QtWidgets
import Qt

import weakref

Expand Down Expand Up @@ -166,7 +167,7 @@ def eventFilter( self, qObject, qEvent ) :
# striving to do all event handling in GafferUI with this
# bubble-up-until-handled methodology anyway, so doing
# shortcuts this way seems to make sense.
if qEvent.type() == qEvent.ShortcutOverride :
if qEvent.type() == QtCore.QEvent.ShortcutOverride :

self.__shortcutAction = None
keySequence = self.__keySequence( qEvent )
Expand All @@ -187,7 +188,7 @@ def eventFilter( self, qObject, qEvent ) :
# We handle the shortcut override event, but that just
# means that we then have the option of handling the
# associated keypress, which is what we do here.
elif qEvent.type() == qEvent.KeyPress :
elif qEvent.type() == QtCore.QEvent.KeyPress :

if self.__shortcutAction is not None and self.__keySequence( qEvent ) in self.__shortcutAction.shortcuts() :
self.__shortcutAction.trigger()
Expand All @@ -207,7 +208,10 @@ def getMenuBar( self ) :

def __keySequence( self, keyEvent ) :

return QtGui.QKeySequence( keyEvent.key() | int( keyEvent.modifiers() ) )
if Qt.__binding__ == "PySide6" :
return QtGui.QKeySequence( keyEvent.keyCombination() )
else :
return QtGui.QKeySequence( keyEvent.key() | int( keyEvent.modifiers() ) )

def __matchingAction( self, keySequence, menu ) :

Expand Down
4 changes: 2 additions & 2 deletions python/GafferUI/MessageWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,14 +754,14 @@ def __setupAppearance( self, expandRows ) :

tableView = self._qtWidget()

tableView.setEditTriggers( tableView.NoEditTriggers )
tableView.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers )
tableView.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows )
tableView.setSelectionMode( QtWidgets.QAbstractItemView.ContiguousSelection )

tableView.verticalHeader().setVisible( False )
tableView.horizontalHeader().setVisible( False )

tableView.setHorizontalScrollMode( tableView.ScrollPerPixel )
tableView.setHorizontalScrollMode( QtWidgets.QTableView.ScrollPerPixel )

tableView.setShowGrid( False )

Expand Down
8 changes: 6 additions & 2 deletions python/GafferUI/MultiLineTextWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from Qt import QtGui
from Qt import QtWidgets
from Qt import QtCore
import Qt

class MultiLineTextWidget( GafferUI.Widget ) :

Expand Down Expand Up @@ -86,7 +87,10 @@ def __init__( self, text="", editable=True, wrapMode=WrapMode.WordOrCharacter, f
self.dropSignal().connect( Gaffer.WeakMethod( self.__drop ) )
self.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ) )

self._qtWidget().setTabStopWidth( 20 ) # pixels
if Qt.__binding__ == "PySide6" :
self._qtWidget().setTabStopDistance( 20 ) # device units
else :
self._qtWidget().setTabStopWidth( 20 ) # pixels

self.__editingFinishedSignal = GafferUI.WidgetSignal()
self.__activatedSignal = GafferUI.WidgetSignal()
Expand Down Expand Up @@ -557,7 +561,7 @@ def minimumSizeHint( self ) :

def event( self, event ) :

if event.type() == event.ShortcutOverride and event == QtGui.QKeySequence.Copy :
if event.type() == QtCore.QEvent.ShortcutOverride and event == QtGui.QKeySequence.Copy :
# QPlainTextEdit doesn't accept this when it's
# read only. so we accept it ourselves, which is
# enough to reenable copying from a read only
Expand Down
5 changes: 3 additions & 2 deletions python/GafferUI/PathListingWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@ def __activated( self, modelIndex ) :

def __selectionChanged( self ) :

self._qtWidget().update()
if not Qt.__binding__ == "PySide6" :
self._qtWidget().update()
self.selectionChangedSignal()( self )

def __pathChanged( self, path ) :
Expand Down Expand Up @@ -989,7 +990,7 @@ def sizeHint( self ) :

def event( self, event ) :

if event.type() == event.ShortcutOverride :
if event.type() == QtCore.QEvent.ShortcutOverride :
if event.key() in ( QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Left, QtCore.Qt.Key_Right ) :
event.accept()
return True
Expand Down
2 changes: 1 addition & 1 deletion python/GafferUI/Slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ def paintEvent( self, event ) :

def event( self, event ) :

if event.type() == event.ShortcutOverride :
if event.type() == QtCore.QEvent.ShortcutOverride :
if event.key() in ( QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace ) :
event.accept()
return True
Expand Down
2 changes: 1 addition & 1 deletion python/GafferUI/SpreadsheetUI/_LinkedScrollBar.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,5 @@ def sliderChange( self, change ) :

QtWidgets.QScrollBar.sliderChange( self, change )

if change == self.SliderStepsChange :
if change == QtWidgets.QAbstractSlider.SliderStepsChange :
self.stepsChanged.emit( self.pageStep(), self.singleStep() )
12 changes: 7 additions & 5 deletions python/GafferUI/SpreadsheetUI/_PlugTableView.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from Qt import QtCore
from Qt import QtWidgets
from Qt import QtCompat
import Qt

from . import _Algo
from . import _ClipboardAlgo
Expand Down Expand Up @@ -139,9 +140,9 @@ def __init__( self, selectionModel, mode, **kw ) :
# the QTableView itself won't edit anything, and we then implement
# our own editing via PlugValueWidgets in _EditWindow.

tableView.setEditTriggers( tableView.NoEditTriggers )
tableView.setSelectionMode( tableView.ExtendedSelection )
tableView.setSelectionBehavior( tableView.SelectItems )
tableView.setEditTriggers( QtWidgets.QTableView.NoEditTriggers )
tableView.setSelectionMode( QtWidgets.QTableView.ExtendedSelection )
tableView.setSelectionBehavior( QtWidgets.QTableView.SelectItems )
self.buttonPressSignal().connect( Gaffer.WeakMethod( self.__buttonPress ) )
self.buttonDoubleClickSignal().connect( Gaffer.WeakMethod( self.__buttonDoubleClick ) )
self.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ) )
Expand All @@ -154,7 +155,7 @@ def __init__( self, selectionModel, mode, **kw ) :

tableView.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff )
tableView.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff )
tableView.setHorizontalScrollMode( tableView.ScrollPerPixel )
tableView.setHorizontalScrollMode( QtWidgets.QTableView.ScrollPerPixel )

tableView.setSizePolicy(
QtWidgets.QSizePolicy.Fixed if mode == self.Mode.RowNames else QtWidgets.QSizePolicy.Maximum,
Expand Down Expand Up @@ -250,7 +251,8 @@ def _displayTransformChanged( self ) :

GafferUI.Widget._displayTransformChanged( self )
# Account for _PlugTableModel's dependency on display transform.
self._qtWidget().update()
if not Qt.__binding__ == "PySide6" :
self._qtWidget().update()

def __applyRowFilter( self ) :

Expand Down
6 changes: 3 additions & 3 deletions python/GafferUI/TabbedContainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ def sizeHint( self ) :

result = QtWidgets.QTabWidget.sizeHint( self )
if self.tabBar().isHidden() :
if self.tabPosition() in ( self.North, self.South ) :
if self.tabPosition() in ( QtWidgets.QTabWidget.North, QtWidgets.QTabWidget.South ) :
result.setHeight( result.height() - self.tabBar().sizeHint().height() )
else :
result.setWidth( result.width() - self.tabBar().sizeHint().width() )
Expand All @@ -360,7 +360,7 @@ def minimumSizeHint( self ) :

result = QtWidgets.QTabWidget.minimumSizeHint( self )
if self.tabBar().isHidden() :
if self.tabPosition() in ( self.North, self.South ) :
if self.tabPosition() in ( QtWidgets.QTabWidget.North, QtWidgets.QTabWidget.South ) :
result.setHeight( result.height() - self.tabBar().minimumSizeHint().height() )
else :
result.setWidth( result.width() - self.tabBar().minimumSizeHint().width() )
Expand All @@ -381,7 +381,7 @@ def __init__( self, sourceWidget, destinationWidget ) :
def eventFilter( self, qObject, qEvent ) :

qEventType = qEvent.type()
if qEventType == qEvent.Show or qEventType == qEvent.Hide :
if qEventType == QtCore.QEvent.Show or qEventType == QtCore.QEvent.Hide :
self.parent().setVisible( qObject.isVisible() )

return False
8 changes: 4 additions & 4 deletions python/GafferUI/TextWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,11 @@ def sizeHint( self ) :

# but then calculate our own width
numChars = self.__fixedCharacterWidth if self.__fixedCharacterWidth is not None else self.__preferredCharacterWidth
textMargins = self.getTextMargins()
contentsMargins = self.getContentsMargins()
textMargins = self.textMargins()
contentsMargins = self.contentsMargins()

width = self.fontMetrics().boundingRect( "M" * numChars ).width()
width += contentsMargins[0] + contentsMargins[2] + textMargins[0] + textMargins[2]
width += contentsMargins.left() + contentsMargins.right() + textMargins.left() + textMargins.right()

options = QtWidgets.QStyleOptionFrame()
self.initStyleOption( options )
Expand All @@ -408,7 +408,7 @@ def sizeHint( self ) :

def event(self, event):

if event.type() == event.ShortcutOverride :
if event.type() == QtCore.QEvent.ShortcutOverride :
if event == QtGui.QKeySequence.Undo :
if not self.isModified() or not self.isUndoAvailable() :
return False
Expand Down
2 changes: 1 addition & 1 deletion python/GafferUI/VectorDataWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ def setData( self, index, value, role ) :
column = self.__columns[index.column()]
column.accessor.setElement( index.row(), column.relativeColumnIndex, value )

if Qt.__binding__ in ( "PySide2", "PyQt5" ) :
if Qt.__binding__ in ( "PySide6", "PySide2", "PyQt5" ) :
self.dataChanged.emit( index, index, [ QtCore.Qt.DisplayRole, QtCore.Qt.EditRole ] )
else:
self.dataChanged.emit( index, index )
Expand Down
3 changes: 2 additions & 1 deletion python/GafferUI/Viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ def __init__( self, edge, **kw ) :
# Remove the SetMinAndMaxSize constraint that our base class added,
# so that we expand to the full width of the viewport, and our toolbar
# is centered inside.
self._qtWidget().layout().setSizeConstraint( self._qtWidget().layout().SetDefaultConstraint )
from Qt.QtWidgets import QLayout
self._qtWidget().layout().setSizeConstraint( QLayout.SetDefaultConstraint )

self.__edge = edge
self.__node = []
Expand Down
Loading
Loading