Search code examples
pythonqtpyqtflickerqdockwidget

pyQt QWidget 'flicker' on resize and redraw


Im looking for a way to minimize redraw 'flicker' on my pyQt app.

My app is a QMainWindow containing two QDockWidget derived objects (and the usual assortment of tool. menu and statusbars). One of the QDockWidgets* uses a QGridLayout to arrange a number of QGLWidget derived windows in each grid element.

When I resize the MainWindow, the entire arrangement resizes correctly. However, in the space between the QGLWidgets I can see what looks like some partially rendered oddly-shaped view of some part of the MainWindow 'flick' into view for a split second and then render correctly. My app is generally colored with dark styles but that flicker in between the QGLWidgets is usually just white and black. The faster I drag the Mainwindow borders, the more pronounced this flicker. And let me emphasize, the 'flicker' is only in the background behind and between the QGLWidget objects, everything else seems fine.

The flicker is there whether I have anything painted in the QGLWidgets or not, so there isn't a heavy load on the rendering.

Is there something I can do to eliminate this flicker on repaint (and resize) events?

I've tried coloring all the widgets the same dark color, played with .hide() and .show() on some of the widgets, but they just seem to slow the process down and make it worse.

  • NOTE I found I had to put a dummy QWidget in the QDockWidget as the QDockWidget wouldn't accept (I guess it doesn't support) Layouts. Maybe that has something to do with this.

This is with pyQt 4.10.1 for python 2.7 running on Windows 7. I have about a months experience with pyQt


Solution

  • SO, following the above advice, the flicker DOES go away when you move to PyQt5 and Python34 and use the new QtWidgets.QOpenGLWidget. You can run the code below that using the new widget and the flicker is gone, but with the old QGLWidget (####lines), the background refreshes are quite annoying during a resize.

    from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
    
    class ImageGLView ( QtWidgets.QOpenGLWidget ):
    ####class ImageGLView ( QtOpenGL.QGLWidget ):   
    
        def __init__(self, parent = None ):
            print ( "ImageGLView.__init__" )
    
            QtWidgets.QOpenGLWidget.__init__( self, parent )
            ####QtOpenGL.QGLWidget.__init__( self, QtOpenGL.QGLFormat( QtOpenGL.QGL.SampleBuffers), parent )
    
            self.setStyleSheet( "QtOpenGL.QGLWidget {border-style: None; background-color: #404040; color:#FFFFFF }" )
            self.setToolTip ( "Image View" )
    
    
    class TopWindow(QtWidgets.QMainWindow):
        def __init__ ( self, parent = None ):
            super( TopWindow, self).__init__(parent)
    
            self.initUI()
            self.initStyle()
    
        def initUI (self):
            self.setGeometry ( 0, 0, 800, 600 )
            self.setWindowTitle ( 'Test pyQt5')
            self.setWindowIcon ( QtGui.QIcon( 'images\Curlyhoward.jpg' ) )
            self.setStyleSheet("QWidget {background-color: #404040; color:#FFFFFF }" )
            self.populateFrames()
    
    
        def initStyle(self):
            palette = self.palette()
            palette.setColor(self.backgroundRole(), QtGui.QColor(64,64,64) )
            self.setToolTip( "Liver Fat Fraction")
            self.setPalette(palette )
    
            self.setDockOptions(QtWidgets.QMainWindow.AnimatedDocks | QtWidgets.QMainWindow.AllowNestedDocks)
    
    
        def populateFrames(self ):       
            self.controlsPane = QtWidgets.QDockWidget()
            self.controlsPane.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable)
            self.controlsPane.setAllowedAreas( QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea )
            self.controlsPane.setStyleSheet("QWidget {background-color: #404040; color:#FFFFFF }" )        
            self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.controlsPane )     
    
            self.imageDisplayPane = QtWidgets.QDockWidget()
            self.imageDisplayPane.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable)
            self.imageDisplayPane.setAllowedAreas( QtCore.Qt.RightDockWidgetArea | QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.TopDockWidgetArea | QtCore.Qt.BottomDockWidgetArea )
            self.imageDisplayPane.setStyleSheet("QWidget {background-color: #FAFAFA; color:#0f0f0f }" )        
    
            numCols = 2
            numRows = 2
    
            self.imageDisplayPane.gridLayout = QtWidgets.QGridLayout()      
    
                #### ...seems like a QDockWidget needs to have a single widget in which it can then accept the layout...
                self.imageDisplayPane.paneContent = QtWidgets.QWidget()
                self.imageDisplayPane.paneContent.setStyleSheet("QWidget {background-color: #404040; color:#FFFFFF }" )
                self.imageDisplayPane.setWidget( self.imageDisplayPane.paneContent )            
                self.imageDisplayPane.paneContent.setLayout( self.imageDisplayPane.gridLayout )                                                                                
    
                for row in range ( numRows ):
                    for col in range ( numCols ):                
                        view = ImageGLView( self )
                        self.imageDisplayPane.gridLayout.addWidget (view, row, col )                        
                self.addDockWidget( QtCore.Qt.LeftDockWidgetArea, self.imageDisplayPane )    
    
    
    def main():
        app = QtWidgets.QApplication( sys.argv )
        app.setStyle ( "windows ")
    
        mainWindow = TopWindow()
        mainWindow.show()
    
        mainWindow.raise_()
        app.exec_()
    
    
    if __name__ == '__main__':
        main()