[Python] Problem with implementation of draggable curves in a PyQtGraph-based widget

Discussão em 'Python' iniciado por Stack, Junho 17, 2025.

  1. Stack

    Stack Membro Participativo

    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...

Compartilhe esta Página