Q&A: How to show a custom cursor on a PyQtGraph plot?

Changing the OS cursor and implementing a custom crosshair
Heads up! You've already completed this tutorial.

When working with PyQtGraph plots, changing the cursor can help with pointing accuracy. For example, if you need to isolate particular points in a scatter plot, a crosshair cursor gives you a more accurate view of where you are pointing than the standard arrow cursor.

In this tutorial, you'll learn two approaches: using Qt's built-in cursor shapes and drawing a fully custom crosshair overlay using PyQtGraph's InfiniteLine.

Changing the mouse cursor on a PyQtGraph PlotWidget

Changing the mouse cursor in a PyQtGraph plot is fairly simple and uses Qt's built-in support for custom cursors over a widget. Since the PyQtGraph PlotWidget is a subclass of QGraphicsWidget (and therefore QWidget) you can use the standard Qt cursor methods.

python
    cursor = Qt.CrossCursor
    self.graphWidget.setCursor(cursor)

For the complete list of cursor types, see the Qt documentation.

The example below shows this in action on a simple PyQtGraph plot.

python
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys  # We need sys so that we can pass argv to QApplication
import os

class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.graphWidget = pg.PlotWidget()
        self.setCentralWidget(self.graphWidget)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        #Add Background colour to white
        self.graphWidget.setBackground('w')
        # Add Title
        self.graphWidget.setTitle("Your Title Here", color="b", size="30pt")
        # Add Axis Labels
        styles = {"color": "#f00", "font-size": "20px"}
        self.graphWidget.setLabel("left", "Temperature (°C)", **styles)
        self.graphWidget.setLabel("bottom", "Hour (H)", **styles)
        #Add legend
        self.graphWidget.addLegend()
        #Add grid
        self.graphWidget.showGrid(x=True, y=True)
        #Set Range
        self.graphWidget.setXRange(0, 10, padding=0)
        self.graphWidget.setYRange(20, 55, padding=0)

        pen = pg.mkPen(color=(255, 0, 0))

        self.graphWidget.plot(hour, temperature, name="Sensor 1",  pen=pen, symbol='+', symbolSize=30, symbolBrush=('b'))

        # Set the cursor for the plotwidget. The mouse cursor will change when over the plot.
        cursor = Qt.CrossCursor
        self.graphWidget.setCursor(cursor)


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec_()

If you run this you'll see a crosshair cursor appear when you hover your mouse over the plot.

Mouse cursor as crosshair on a PyQtGraph plot Crosshair cursor on a PyQtGraph PlotWidget

Drawing a custom crosshair cursor with InfiniteLine

If you want to show a completely custom cursor — for example, a full-size crosshair overlay spanning the entire plot — things are a little trickier. In this case you need to draw the cursor yourself by adding elements to the plot and then listening for mouse events to update their position.

The code below is an updated version of the plot above, with two crosshair lines added using PyQtGraph's InfiniteLine objects. The lines are created in the __init__ method and then updated in the update_crosshair method whenever the mouse moves.

You could also add any other QGraphicsScene item here instead.

The important part is the SignalProxy which forwards mouse movement signals to our custom update_crosshair method. The rateLimit parameter sets the maximum number of signals per second which will be sent, to avoid overloading our handler/app with excessive events.

python
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import Qt
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys  # We need sys so that we can pass argv to QApplication
import os


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.graphWidget = pg.PlotWidget()
        self.setCentralWidget(self.graphWidget)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        #Add Background colour to white
        self.graphWidget.setBackground('w')
        # Add Title
        self.graphWidget.setTitle("Your Title Here", color="b", size="30pt")
        # Add Axis Labels
        styles = {"color": "#f00", "font-size": "20px"}
        self.graphWidget.setLabel("left", "Temperature (°C)", **styles)
        self.graphWidget.setLabel("bottom", "Hour (H)", **styles)
        #Add legend
        self.graphWidget.addLegend()
        #Add grid
        self.graphWidget.showGrid(x=True, y=True)
        #Set Range
        self.graphWidget.setXRange(0, 10, padding=0)
        self.graphWidget.setYRange(20, 55, padding=0)

        pen = pg.mkPen(color=(255, 0, 0))
        self.graphWidget.plot(hour, temperature, name="Sensor 1",  pen=pen, symbol='+', symbolSize=30, symbolBrush=('b'))

        # Add crosshair lines.
        self.crosshair_v = pg.InfiniteLine(angle=90, movable=False)
        self.crosshair_h = pg.InfiniteLine(angle=0, movable=False)
        self.graphWidget.addItem(self.crosshair_v, ignoreBounds=True)
        self.graphWidget.addItem(self.crosshair_h, ignoreBounds=True)

        self.proxy = pg.SignalProxy(self.graphWidget.scene().sigMouseMoved, rateLimit=60, slot=self.update_crosshair)

    def update_crosshair(self, e):
        pos = e[0]
        if self.graphWidget.sceneBoundingRect().contains(pos):
            mousePoint = self.graphWidget.getPlotItem().vb.mapSceneToView(pos)
            self.crosshair_v.setPos(mousePoint.x())
            self.crosshair_h.setPos(mousePoint.y())


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec_()

If you run this example, in addition to the mouse cursor you'll also see a faint yellow line overlaying the plot.

Drawing custom crosshair cursor lines on a PyQtGraph plot Custom crosshair overlay with the default mouse cursor still visible

Hiding the default mouse cursor over the plot

To hide the default mouse cursor over the plot, set the cursor type to Qt.BlankCursor. This gives you a clean crosshair-only cursor experience.

python
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # ... rest of __init__ as before

        # Hide the cursor over the plot.
        cursor = Qt.BlankCursor
        self.graphWidget.setCursor(cursor)

This will give the following result when hovering over the plot.

Custom crosshair cursor with default mouse cursor hidden on PyQtGraph plot The custom crosshair cursor with the default mouse cursor hidden

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

PyQt/PySide 1:1 Coaching with Martin Fitzpatrick

Save yourself time and frustration. Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.

Book Now 60 mins ($195)

Martin Fitzpatrick

Q&A: How to show a custom cursor on a PyQtGraph plot? was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.