I am trying to implement a custom pyqtgraph-based widget, which has 3 different y-axes and DraggablePlotCurveItems added to the viewboxes corresponding to the different y-axes. The DraggablePlotCurveItems allows the user to drag the CurveItem along the x-axis relative to other curves (it does this by updating the x data of the curve with the drag action). The different viewboxes overlaying each other interfere with the dragging action, so I added controls to change the Z-values of the viewbox containing the curve I want to drag. For some reason, my code only partially works: 1 - The curve in the first viewbox can only be dragged when I zoom all the way in to the beginning of the curve and drag it from there, otherwise it cannot be dragged, which is a weird behavior. 2 - the curves in the other two viewboxes can be dragged, but sometimes grabbing the curve from a certain region does not work, and you have to try a different portion of the curve. Minimal working example: import sys import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui, QtWidgets import pandas as pd pg.setConfigOptions(antialias=True) class DraggablePlotCurveItem(pg.PlotCurveItem): """ A PlotDataItem that can be dragged horizontally. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setAcceptHoverEvents(True) self.drag_start_pos = None self.original_x_data = None self.original_y_data = None self.drag_tolerance = 4 def mouseDragEvent(self, ev): if ev.button() != QtCore.Qt.MouseButton.LeftButton: ev.ignore() return if ev.isStart(): if self.mouseShape().contains(ev.pos()): self.drag_start_pos = ev.pos() self.original_x_data = self.xData.copy() self.original_y_data = self.yData.copy() ev.accept() else: ev.ignore() elif ev.isFinish(): self.drag_start_pos = None self.original_x_data = None self.original_y_data = None ev.accept() else: if self.drag_start_pos is None: ev.ignore() return delta = ev.pos() - self.drag_start_pos new_x = self.original_x_data + delta.x() new_y = self.original_y_data # + delta.y() # Update the plot with the new data self.setData(x=new_x, y=new_y) ev.accept() def shape(self): path = super().shape() stroker = QtGui.QPainterPathStroker() stroker.setWidth(self.drag_tolerance) return stroker.createStroke(path) if __name__ == "__main__": import numpy as np data = pd.read_csv( "PDO_SR_367_STG1_HMMQU5_ASCII_Oilfield.txt", sep="\t", ) data["Time"] = pd.to_datetime(data["Time"], format="%m:%d:%Y:%H:%M:%S") time = (data["Time"].astype("int64") // 10**9).values pressure = data["Treating Pressure (psi)"].values # [5000:10000] rate = data["SLUR RATE (bbl/min)"].values # [5000:10000] proppant_surf = data["PROP CON (PPA)"].values # [5000:10000] proppant_bh = data["BH PROP CON (PPA)"].values # [5000:10000] app = pg.mkQApp("Multiple Axes with Draggable Curves") main_widget = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout(main_widget) button_layout = QtWidgets.QHBoxLayout() # Create buttons drag_axis1_btn = QtWidgets.QPushButton("Drag Axis 1") drag_axis2_btn = QtWidgets.QPushButton("Drag Axis 2") drag_axis3_btn = QtWidgets.QPushButton("Drag Axis 3") button_layout.addWidget(drag_axis1_btn) button_layout.addWidget(drag_axis2_btn) button_layout.addWidget(drag_axis3_btn) button_layout.addStretch() pw = pg.PlotWidget() layout.addLayout(button_layout) layout.addWidget(pw) main_widget.show() main_widget.setWindowTitle( "pyqtgraph example: MultiplePlotAxes with Draggable Curves" ) master_plot = pw.plotItem master_plot.vb.setMouseEnabled(x=True, y=False) p1 = pg.ViewBox() master_plot.showAxis("left") master_plot.scene().addItem(p1) master_plot.getAxis("left").linkToView(p1) p1.setXLink(master_plot) master_plot.getAxis("left").setLabel("axis 1", color="red") p1.setMouseEnabled(x=True, y=False) p2 = pg.ViewBox() master_plot.showAxis("right") master_plot.scene().addItem(p2) master_plot.getAxis("right").linkToView(p2) p2.setXLink(master_plot) master_plot.getAxis("right").setLabel("axis 2", color="#0000ff") p3 = pg.ViewBox() ax3 = pg.AxisItem("right") master_plot.layout.addItem(ax3, 2, 3) master_plot.scene().addItem(p3) master_plot.vb.setMouseEnabled(x=True, y=False) p2.setMouseEnabled(x=True, y=False) p3.setMouseEnabled(x=True, y=False) ax3.linkToView(p3) p3.setXLink(master_plot) ax3.setLabel("axis 3", color="#ff0000") def drag_axis1_clicked(): p1.setZValue(9999) p2.setZValue(1) p3.setZValue(1) print("Axis 1 is now draggable") def drag_axis2_clicked(): p1.setZValue(1) p2.setZValue(9999) p3.setZValue(1) print("Axis 2 is now draggable") def drag_axis3_clicked(): p1.setZValue(1) p2.setZValue(1) p3.setZValue(9999) print("Axis 3 is now draggable") drag_axis1_btn.clicked.connect(drag_axis1_clicked) drag_axis2_btn.clicked.connect(drag_axis2_clicked) drag_axis3_btn.clicked.connect(drag_axis3_clicked) def updateViews(): p1.setGeometry(master_plot.vb.sceneBoundingRect()) p2.setGeometry(master_plot.vb.sceneBoundingRect()) p3.setGeometry(master_plot.vb.sceneBoundingRect()) p1.linkedViewChanged(master_plot.vb, p1.XAxis) p2.linkedViewChanged(master_plot.vb, p2.XAxis) p3.linkedViewChanged(master_plot.vb, p3.XAxis) updateViews() master_plot.vb.sigResized.connect(updateViews) curve1 = DraggablePlotCurveItem( x=time, y=pressure, pen=pg.mkPen("r", width=1), name="Curve 1" ) curve2 = DraggablePlotCurveItem( x=time, y=rate, pen=pg.mkPen("b", width=1), name="Curve 2" ) curve3 = DraggablePlotCurveItem( x=time, y=proppant_surf, pen=pg.mkPen("g", width=1), name="Curve 3" ) curve4 = DraggablePlotCurveItem( x=time, y=proppant_bh, pen=pg.mkPen("g", width=1), name="Curve 4" ) p1.addItem(curve1) p2.addItem(curve2) p3.addItem(curve3) p3.addItem(curve4) p1.autoRange() pg.exec() Data file used for the plot I am expecting dragging the different curves to be easier and smoother: the user should be able to focus on one viewbox, and grab any curve in that viewbox and drag it. The current implementation works partially. Continue reading...