infliniteLine label value(s)

610 views
Skip to first unread message

Jacob Bernard

unread,
Nov 30, 2018, 1:52:37 PM11/30/18
to pyqtgraph
I have an infiniteline on my graph, and have figured out how to use value to get the value of the current axis: 

infline = pg.InfiniteLine(pos=1000, movable=True, label='{value:0.2fms}')

However I would like to have my line vertically as it is now, but display the y values of my curves. Is this possible? I know that if I make the line horizontal it will give the y value, but I'd like for it to remain vertical. 


Patrick

unread,
Nov 30, 2018, 10:27:03 PM11/30/18
to pyqtgraph
Hi,

You can connect to signals emitted by the infiniteline when it is moved. Example code snippet below:


# Assume plot data is stored in arrays of xcoords, yvalues
self.xcoords = np.linspace(0.1, 10, 10)
self.yvalues = np.log(self.xcoords)


# Create the infinite line to select an x coordinate, connect to its moved signal
self.crosshairx = pg.InfiniteLine(angle=90, movable=True)
self.crosshairx.sigPositionChanged.connect(self._crosshairx_changed)


# Keep track of index of currently selected x coordinate, so we can avoid doing
# unnecessary updates when the infiniteline changes but the index of the
# closest x/y data point is still the same
self.selected_x_i = np.argmin(np.abs(self.xcoords - self.crosshairx.value()))


# Slot to get y value of point closest to x value of the infinite line
def _crosshairx_changed(self):
    new_i
= np.argmin(np.abs(self.xcoords - self.crosshairx.value()))
   
if new_i != self.selected_x_i:
       
self.selected_x_i = new_i
       
# Do something interesting
       
print(self.yvalues[self.selected_x_i])


Patrick

Jacob Bernard

unread,
Dec 18, 2018, 12:34:01 AM12/18/18
to pyqtgraph
Hi Patrick, 

Thanks for taking the time to respond! Sorry I didn't reply sooner. I attempted to modify and implement your example, but I'm unable to get it to work properly: 

    def cursorLine(self):
        self.selected_x_i = 0
        self.selected_x = str(self.curvesData.get(0)[self.selected_x_i])

        # Create the infinite line to select an x coordinate, connect to its moved signal
        self.crosshairx = pg.InfiniteLine(angle=90, movable=True, label=self.selected_x)
        self.crosshairx.sigPositionChanged().connect(self._crosshairx_changed)

        self.crosshairx.setBounds(bounds=(0,len(self.curvesData.get(0))))

        # Keep track of index of currently selected x coordinate, so we can avoid doing
        # unnecessary updates when the infiniteline changes but the index of the
        # closest x/y data point is still the same
        #self.selected_x_i = np.argmin(np.abs(len(self.curvesData.get(0)) - self.crosshairx.value()))

        self.plotItem.scene().addItem(self.crosshairx)


    def _crosshairx_changed(self):
        new_i = np.argmin(np.abs(len(self.curvesData.get(0)) - self.crosshairx.value()))
        if new_i != self.selected_x_i:
            self.selected_x_i = new_i
            self.selected_x = str(self.curvesData.get(0)[self.selected_x_i])
            print(self.curvesData(0)[self.selected_x_i])

Your emitting the setting of the label confused me a bit, but I understand the idea of the example in that you keep track of when the line is moved and thus change which X value you should be accessing. It seemed that there wasn't actually a connection between the line being moved and _crosshairx_changed() so I added the parenthesis after sigPositionChanged. This results in the error "TypeError: native Qt signal is not callable". Sorry about asking for help again, but I'm really not sure where to go from here. 

Kind Regards,

Jacob

Patrick

unread,
Dec 18, 2018, 1:50:20 AM12/18/18
to pyqtgraph
Hi Jacob,

You don't want the parenthesis after sigPositionChanged, the connection to the signal was working just fine. The issue is a bug so that your new_i is not updating properly (it's always zero), so nothing appears to happen.
The bug is in the logic for computing new_i (and selected_x_i). Originally, the line was:

self.selected_x_i = np.argmin(np.abs(self.xcoords - self.crosshairx.value()))

That assumes the x-axis coordinates/labels (eg, sample time values) are stored in the array xcoords. The code then finds the array index holding the x value closest to the position of the InfiniteLine. You are just using the sample numbers as the x coordinate, which is fine, but what you have:

self.selected_x_i = np.argmin(np.abs(len(self.curvesData.get(0)) - self.crosshairx.value()))

uses len(...), which is a single value (array length of 1), so the array index is always zero. Change it to a range of the array index values:

self.selected_x_i = np.argmin(np.abs(np.arange(len(self.curvesData.get(0))) - self.crosshairx.value()))

or, if you will always be indexing the x-axis values by sample number, simply 

self.selected_x_i = int(np.round(self.crosshairx.value()))

Other issues:
Add the InfiniteLine to the plotItem, not the scene.
Bounds for InfiniteLine should be len(data) - 1.

So code looks like:

#!/usr/bin/env python3

from PyQt5 import QtWidgets
import pyqtgraph as pg
import numpy as np

class CurveData():
   
def __init__(self):
       
self.data = np.random.rand(100)
   
def get(self, index):
       
return self.data

class TestPlot(pg.GraphicsLayoutWidget):

   
def __init__(self):
       
super().__init__()
       
self.curvesData = CurveData()
       
self.plotItem = self.addPlot()
       
self.plotItem.plot(self.curvesData.get(0))
       
self.cursorLine()


   
def cursorLine(self):
       
self.selected_x_i = 0


       
# Create the infinite line to select an x coordinate, connect to its moved signal
       
self.crosshairx = pg.InfiniteLine(angle=90, movable=True, label="{:0.2f}".format(self.curvesData.get(0)[self.selected_x_i]))
       
self.crosshairx.sigPositionChanged.connect(self._crosshairx_changed)

       
self.crosshairx.setBounds(bounds=(0,len(self.curvesData.get(0)) - 1))


       
# Keep track of index of currently selected x coordinate, so we can avoid doing
       
# unnecessary updates when the infiniteline changes but the index of the
       
# closest x/y data point is still the same

       
self.selected_x_i = int(np.round(self.crosshairx.value()))
       
self.plotItem.addItem(self.crosshairx)

   
def _crosshairx_changed(self):
        new_i
= int(np.round(self.crosshairx.value()))

       
if new_i != self.selected_x_i:
           
self.selected_x_i =
new_i
           
self.crosshairx.label.setFormat("{:0.2f}".format(self.curvesData.get(0)[self.selected_x_i]))

def main():
   
import sys
    app
= QtWidgets.QApplication(sys.argv)
    mainwindow
= TestPlot()
    mainwindow
.show()
    sys
.exit(app.exec_())

if __name__ == '__main__':
    main
()


Patrick


Reply all
Reply to author
Forward
0 new messages