Search code examples
pythonpyqtpyqt5pyqtgraph

How to deselect curve when clicking on background in pyqtgraph


I try to deselect a curve by clicking on the background in a pyqtgraph. So when I click on a curve, it is selecting it and when I click on the background or the curve again, it should deselect it. If there is no curve selected at the moment and I click on the background, it should do nothing.

Clicking and selecting/deselecting a curve by clicking the curve is working perfectly fine, here a small example:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg


class GraphWidget(pg.GraphicsLayoutWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.plot = self.addPlot()
        self.curves = []
        self.selected_curve = None

    def add_curve(self, x, y, pen):
        curve = self.plot.plot(x, y, pen=pen)
        curve.setCurveClickable(True)
        curve.sigClicked.connect(self.curve_click)
        self.curves.append(curve)

    def curve_click(self, curve):
        curve_inst = None

        # Get curve instance
        for line_item in self.curves:
            if line_item == curve:
                curve_inst = line_item

        if curve == self.selected_curve:
            self._reset_curve_properties(curve)
            self.selected_curve = None
            return

        for line_item in self.curves:
            pen = line_item.opts['pen']
            pen = pg.mkPen(pen)
            if line_item == curve_inst:
                selected_pen = pg.mkPen(color=pen.color(), width=2)
                line_item.setPen(selected_pen)
                self.selected_curve = line_item
            else:
                default_pen = pg.mkPen(color=pen.color(), width=1)
                line_item.setPen(default_pen)

    def _reset_curve_properties(self, curve):
        pen = curve.opts['pen']
        default_pen = pg.mkPen(color=pen.color(), width=1)
        curve.setPen(default_pen)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('PyQtGraph Clickable Curves')
        self.graph_widget = GraphWidget()
        self.setCentralWidget(self.graph_widget)

        # Add some test data
        x = list(range(10))
        y1 = [i for i in x]
        y2 = [i ** 2 for i in x]
        self.graph_widget.add_curve(x, y1, pen='r')
        self.graph_widget.add_curve(x, y2, pen='b')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

Now I just need to accomplish that it is also deselecting it when clicking the background.

I connected the mouse click:

self.plot.scene().sigMouseClicked.connect(self.on_mouse_clicked)

Then in on_mouse_clicked getting the items at clicked position:

items = self.plot_widget.scene().items(event.scenePos())

But this only gives me all items in the viewBox (or in a certain range where I clicked). I checked if there are no PlotCurveItems in the returned list; if there is no curve in this list, then it should deselect, because this means that I clicked on the background.

This doesn't work properly though, I have to zoom out a lot and click somewhere further away such that it doesn't recognize any curve anymore.

Also with .itemAt() it isn't working. In my original program I have a crosshair cursor with a horizontal and vertical DashLine, so this is what I get in return (.InfiniteLine), as this is the top most item.

Is it possible at all to recognize the background click?


Solution

  • You can use event.isAccepted() to check if the mouse position was on a plot item, like the following.

    ...
    class GraphWidget(pg.GraphicsLayoutWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.plot = self.addPlot()
            self.curves = []
            self.selected_curve = None
            def on_click_scene(event):
                if not event.isAccepted():
                    if self.selected_curve:
                        self._reset_curve_properties(self.selected_curve)
                        self.selected_curve = None
            self.scene().sigMouseClicked.connect(on_click_scene)
    ...