Search code examples
pythonpyqtgraphpyside6

Hover Tool for plots in Pyqtgraph


I want to have data information shown when hovering over a line in pyqtgraph plots, but can't get my sigpointsHovered to emit any signal. Here is a simple example of what i tried to do:

from PySide6.QtWidgets import QMainWindow, QWidget, QApplication, QVBoxLayout
import pyqtgraph as pg

def hovered(self, points, ev):
    print("FOOO")
    
x = [1,2,3,4,5,6,7,8,9]
y = [0,1,2,3,4,5,6,7,8]

app = QApplication([])
window = QWidget()
layout = QVBoxLayout()

plot_widget = pg.PlotWidget()
plot_item = plot_widget.getPlotItem()
line = plot_item.plot(x,y)
line.sigPointsHovered.connect(hovered)

layout.addWidget(plot_widget)
window.setLayout(layout)
       
window.show()
app.exec_()

I have already tried setting "hoverable" = True and read the docs several times, but I honestly have no clue why the sigPointsHovered is not working.


Solution

  • Why sigPointsHovered is not working

    In short: There is no way to set "hoverable" argument for PlotDataItem class's ScatterPlotItem right now. Therefore, it is not possible to use sigPointsHovered.

    You could see this in the source code of PlotDataItem class's function updateItems.

    enter image description here

    Workarounds

    1. If you really want something like sigPointsHovered right now, instead of using a PlotWiget, use a ScatterPlotItem and set hoverable = True when you initialize it or when you use setData function. Run python -m pyqtgraph.examples and find the scatter plot example to see some example codes.

    2. However, from your description, I think you actually want to do something when you hover over a "cruve" (instead of points). Currently, PlotCurveItem doesn't implement a hoverEvent, so you may try to make a class that inherits the PlotCurveItem and add a hoverEvent to it.

    Let me show you how to do this.

    In this example, when the cursor enters the curve, the color changes to blue, and turn back to white when it leave the curve.

    import pyqtgraph as pg
    from pyqtgraph import QtCore, QtGui
    
    
    class HoverableCurveItem(pg.PlotCurveItem):
        sigCurveHovered = QtCore.Signal(object, object)
        sigCurveNotHovered = QtCore.Signal(object, object)
    
        def __init__(self, hoverable=True, *args, **kwargs):
            super(HoverableCurveItem, self).__init__(*args, **kwargs)
            self.hoverable = hoverable
            self.setAcceptHoverEvents(True)
    
        def hoverEvent(self, ev):
            if self.hoverable:
                if self.mouseShape().contains(ev.pos()):
                    self.sigCurveHovered.emit(self, ev)
                else:
                    self.sigCurveNotHovered.emit(self, ev)
    
    
    class MainWindow(QtGui.QMainWindow):
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
    
            self.view = pg.GraphicsLayoutWidget()
            self.setCentralWidget(self.view)
            self.makeplot()
    
        def makeplot(self):
            x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
            y = [0, 1, 2, 3, 4, 5, 6, 7, 8]
            plot = self.view.addPlot()
            self.plotitem = HoverableCurveItem(x, y, pen=pg.mkPen('w', width=10))
            self.plotitem.setClickable(True, width=10)
            self.plotitem.sigCurveHovered.connect(self.hovered)
            self.plotitem.sigCurveNotHovered.connect(self.leaveHovered)
            plot.addItem(self.plotitem)
    
        def hovered(self):
            print("cursor entered curve")
            self.plotitem.setPen(pg.mkPen('b', width=10))
    
        def leaveHovered(self):
            self.plotitem.setPen(pg.mkPen('w', width=10))
    
    
    if __name__ == '__main__':
        import sys
    
        app = QtGui.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec())
    
    

    Edit

    Need to setAcceptHoverEvent to True

    Also, in the updated example, when the cursor enters the curve, the color changes to blue, and turn back to white when it leave the curve.