Search code examples
pythonpyqtpyqtgraph

PyQtGraph get color of pixel being moused over


I have some code very similar to the example given by PyQtGraph below. I also want to add to the label the color of the exact pixel being moused over (the pixel at the crosshair). How would I do this?

import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph.Point import Point

#generate layout
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
win.setWindowTitle('pyqtgraph example: crosshair')
label = pg.LabelItem(justify='right')
win.addItem(label)
p1 = win.addPlot(row=1, col=0)
p2 = win.addPlot(row=2, col=0)

region = pg.LinearRegionItem()
region.setZValue(10)
# Add the LinearRegionItem to the ViewBox, but tell the ViewBox to exclude this 
# item when doing auto-range calculations.
p2.addItem(region, ignoreBounds=True)

#pg.dbg()
p1.setAutoVisible(y=True)


#create numpy arrays
#make the numbers large to show that the xrange shows data from 10000 to all the way 0
data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)
data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)

p1.plot(data1, pen="r")
p1.plot(data2, pen="g")

p2.plot(data1, pen="w")

def update():
    region.setZValue(10)
    minX, maxX = region.getRegion()
    p1.setXRange(minX, maxX, padding=0)    

region.sigRegionChanged.connect(update)

def updateRegion(window, viewRange):
    rgn = viewRange[0]
    region.setRegion(rgn)

p1.sigRangeChanged.connect(updateRegion)

region.setRegion([1000, 2000])

#cross hair
vLine = pg.InfiniteLine(angle=90, movable=False)
hLine = pg.InfiniteLine(angle=0, movable=False)
p1.addItem(vLine, ignoreBounds=True)
p1.addItem(hLine, ignoreBounds=True)


vb = p1.vb

def mouseMoved(evt):
    pos = evt[0]  ## using signal proxy turns original arguments into a tuple
    if p1.sceneBoundingRect().contains(pos):
        mousePoint = vb.mapSceneToView(pos)
        index = int(mousePoint.x())

        #######
        GET THE PIXEL COLOR AND ADD TO LABEL??? HOW?
        #######

        if index > 0 and index < len(data1):
            label.setText("<span style='font-size: 12pt'>x=%0.1f,   <span style='color: red'>y1=%0.1f</span>,   <span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(), data1[index], data2[index]))
        vLine.setPos(mousePoint.x())
        hLine.setPos(mousePoint.y())


proxy = pg.SignalProxy(p1.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved)
#p1.scene().sigMouseMoved.connect(mouseMoved)


## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

Solution

  • You must override the mouseMoveEvent method, record the widget and get the pixel.

    For this we will create a class that inherits from pg.GraphicsWindow:

    class MyGraphicsWindow(pg.GraphicsWindow):
        def __init__(self, parent=None):
            pg.GraphicsWindow.__init__(self, parent=parent)
            self.setMouseTracking(True)
    
        def mouseMoveEvent(self, e):
            image = QtGui.QPixmap.grabWidget(self).toImage()
            color = QtGui.QColor(image.pixel(e.pos()))
            print(color.name())
            print("red:{}, green:{}, blue:{}".format(color.red(), color.green(), color.blue()))
            pg.GraphicsWindow.mouseMoveEvent(self, e)
    

    Then we will replace win = pg.GraphicsWindow() with win = MyGraphicsWindow()

    Complete code:

    class MyGraphicsWindow(pg.GraphicsWindow):
        def __init__(self, parent=None):
            pg.GraphicsWindow.__init__(self, parent=parent)
            self.setMouseTracking(True)
    
        def mouseMoveEvent(self, e):
            image = QtGui.QPixmap.grabWidget(self).toImage()
            color = QtGui.QColor(image.pixel(e.pos()))
            print(color.name())
            print("{}, {}, {}".format(color.red(), color.green(), color.blue()))
            pg.GraphicsWindow.mouseMoveEvent(self, e)
    
    
    #generate layout
    app = QtGui.QApplication(sys.argv)
    win = MyGraphicsWindow()
    win.setWindowTitle('pyqtgraph example: crosshair')
    label = pg.LabelItem(justify='right')
    win.addItem(label)
    p1 = win.addPlot(row=1, col=0)
    p2 = win.addPlot(row=2, col=0)
    
    region = pg.LinearRegionItem()
    region.setZValue(10)
    # Add the LinearRegionItem to the ViewBox, but tell the ViewBox to exclude this 
    # item when doing auto-range calculations.
    p2.addItem(region, ignoreBounds=True)
    
    #pg.dbg()
    p1.setAutoVisible(y=True)
    
    
    #create numpy arrays
    #make the numbers large to show that the xrange shows data from 10000 to all the way 0
    data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)
    data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)
    
    p1.plot(data1, pen="r")
    p1.plot(data2, pen="g")
    
    p2.plot(data1, pen="w")
    
    def update():
        region.setZValue(10)
        minX, maxX = region.getRegion()
    
    sys.exit(app.exec_())