Search code examples
pyqtpyqtgraph

Order of placement of widgets in PyQt: Can I place a GraphicalLayoutWidget on a QHBoxLayout?


I'm new to PyQt and pyqtgraph and have a heatplot, which I'd like to place on a widget (if that's the right term), adjacent to which a slider will eventually appear. The code I have thus far, which is a modification of something I've shamelessly copied from an online tutorial, is as follows:

import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, mkQApp
from PyQt5.QtWidgets import QHBoxLayout

from pyqtgraph.Qt import QtGui, QtCore

pg.setConfigOption('background', 'lightgray')
pg.setConfigOption('foreground','black')

font = QtGui.QFont("Times", 18)

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
   
        layout = QHBoxLayout()

        graph_widget = pg.GraphicsLayoutWidget(show=True)
        self.setCentralWidget(graph_widget)
    
        self.setWindowTitle('pyqtgraph example: heatmap display')
        self.setStyleSheet("background-color: lightgray;")
        self.resize(1000,1000)

        #layout.addWidget(graph_widget) # Where my error occurs

        self.show()

        corrMatrix = np.array([
            [ 1.        ,  0.5184571 , -0.70188642],
            [ 0.5184571 ,  1.        , -0.86094096],
            [-0.70188642, -0.86094096,  1.        ]
        ])
        columns = ["A", "B", "C"]

        pg.setConfigOption('imageAxisOrder', 'row-major') 
    
        correlogram = pg.ImageItem()

        tr = QtGui.QTransform().translate(-0.5, -0.5) 
        correlogram.setTransform(tr)
        correlogram.setImage(corrMatrix)

        plotItem = graph_widget.addPlot() 
    
        plotItem.invertY(True)           
        plotItem.setDefaultPadding(0.0)  
        plotItem.addItem(correlogram)    
    
        plotItem.showAxes( True, showValues=(True, True, False, False), size=40 )

        ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
        for side in ('left','top','right','bottom'):
            plotItem.getAxis(side).setTicks( (ticks, []) ) 
            plotItem.getAxis(side).setTickFont(font) 
        plotItem.getAxis('bottom').setHeight(10) 
           
        colorMap = pg.colormap.get("CET-D1")     
        bar = pg.ColorBarItem( interactive=False,values=(0,1), colorMap=colorMap)
        bar.setImageItem(correlogram, insert_in=plotItem)    

mkQApp("Correlation matrix display")
main_window = MainWindow()

if __name__ == '__main__':
    pg.exec()

The result is shown below:

enter image description here

Eventually I would like to place the above in a layout, in which a row contains my plot, a slider (and a few other widgets). A TypeError message results when I un-comment the line layout.addWidget(graph_widget). The message states

TypeError: addWidget(self, QWidget, stretch: int = 0, alignment: Union[Qt.Alignment, 
Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 
'GraphicsLayoutWidget'

Is it not possible to place a GraphicsLayoutWidget on a QHBoxLayout()? If so, what's the correct way to organize things so I have my graph adjacent to which I can place sliders, line edits, etc.


Solution

  • One issue is that you are setting your graph_widget as the central widget for your QMainWindow instance and then later adding it to a layout with the intention of adding more widgets.

    I think you are more likely to achieve the results you are looking for if you set a generic QWidget as the window's central widget, set a layout, and then add the graph_widget and any other widgets to the layout.

    Here is an example using your code and the solution from @musicmante in the comments, and adding a vertical slider:

    from PyQt5.QtWidgets import QHBoxLayout, QWidget, QSlider #  Import PyQt5 first before pyqtgraph
    from pyqtgraph.Qt import QtWidgets, mkQApp
    import pyqtgraph as pg
    import numpy as np
    
    from pyqtgraph.Qt import QtGui, QtCore
    
    pg.setConfigOption('background', 'lightgray')
    pg.setConfigOption('foreground','black')
    
    font = QtGui.QFont("Times", 18)
    
    class MainWindow(QtWidgets.QMainWindow):
    
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
            self.central = QWidget()                          # create a QWidget
            slider = QSlider(orientation=QtCore.Qt.Vertical)  # Vertical Slider
    
            graph_widget = pg.GraphicsLayoutWidget(show=True)
            self.setWindowTitle('pyqtgraph example: heatmap display')
            self.setStyleSheet("background-color: lightgray;")
            self.resize(1000,1000)
    
            self.setCentralWidget(self.central)  # set the QWidget as centralWidget
            layout = QHBoxLayout(self.central)  # assign layout to central widget
    
            layout.addWidget(graph_widget) # No More error
            layout.addWidget(slider)       # add a slider
    
            self.show()
    
            corrMatrix = np.array([
                [ 1.        ,  0.5184571 , -0.70188642],
                [ 0.5184571 ,  1.        , -0.86094096],
                [-0.70188642, -0.86094096,  1.        ]
            ])
            columns = ["A", "B", "C"]
    
            pg.setConfigOption('imageAxisOrder', 'row-major')
    
            correlogram = pg.ImageItem()
    
            tr = QtGui.QTransform().translate(-0.5, -0.5)
            correlogram.setTransform(tr)
            correlogram.setImage(corrMatrix)
    
            plotItem = graph_widget.addPlot()
    
            plotItem.invertY(True)
            plotItem.setDefaultPadding(0.0)
            plotItem.addItem(correlogram)
    
            plotItem.showAxes( True, showValues=(True, True, False, False), size=40 )
    
            ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
            for side in ('left','top','right','bottom'):
                plotItem.getAxis(side).setTicks( (ticks, []) )
                plotItem.getAxis(side).setTickFont(font)
            plotItem.getAxis('bottom').setHeight(10)
    
            colorMap = pg.colormap.get("CET-D1")
            bar = pg.ColorBarItem( interactive=False,values=(0,1), colorMap=colorMap)
            bar.setImageItem(correlogram, insert_in=plotItem)
    
    
    mkQApp("Correlation matrix display")
    main_window = MainWindow()
    
    if __name__ == '__main__':
        pg.exec()