Change single tick color + possible PlotDataItem bug?

86 views
Skip to first unread message

Christopher Mullins

unread,
Jul 27, 2017, 6:02:23 PM7/27/17
to pyqtgraph
Hi all,

(1) Question is what it sounds like.  The situation is that I'm prototyping an EEG analysis application, and I'm trying to simulate the "Select bad channels" functionality in EEGLAB (a MATLAB GUI for this).  This involves plotting sixty-something data channels together separated vertically, labeling the y-ticks with the name of each channel.  When a user clicks a channel's data, the color should change (see minimal working example below).

The trouble is it's easy to see that a certain channel of data is bad, but sometimes difficult to tell exactly which channel that is ie. the name of the channel. So I'd like to change the color of the tick label as well.  Is there a way to specify just _one_ particular tick and change its color?

Happy for any further suggestions on the code.  Run it by pasting it into a file and running python -i filename.py.

(2) If you look at the comment I've made in my code, I realized that in order for the sigClicked signal to fire, you need to use a PlotCurveItem (as in the MouseSelection.py example).  If you use a PlotDataItem (which is returned from PlotItem.plot() [1]) the function connected to the sigClicked signal won't fire.  Is this expected behavior, or a bug? 

Thanks,
Chris

import pyqtgraph as pq
import numpy as np
win
= pq.GraphicsWindow(title="Basic EEG example")
eegplot
= win.addPlot(title="Multiple curves")

# Set up the data sort of like I'll be interacting with it in my application
channel_names
= ['Chan1', 'Chan2', 'Chan3']
channel_data
= [np.sin(np.linspace(0, 20, 1000)), np.sin(np.linspace(1, 21, 1000)), np.sin(np.linspace(2, 22, 1000))]
chan_name_to_data
= dict(zip(channel_names, channel_data))
chan_name_to_curve
= dict()

def changeChannelColor(curve):
   
print("Firing changeChannelColor...")
    curve
.setPen('r')

ticklabels
= []
for index, channel_name in enumerate(channel_names):
    data
= chan_name_to_data[channel_name]
    data
-= np.mean(data, axis=0)
   
# separate the channels vertically
    offset
= index*10
    data
+= offset
    ticklabels
.append((offset, channel_name))
    datacurve
= pq.PlotCurveItem(y=chan_name_to_data[channel_name], pen='b', clickable=True)
   
# If you use a PlotDataItem instead, sigClicked does not fire.
   
#datacurve = pq.PlotDataItem(y=chan_name_to_data[channel_name], pen='b', clickable=True)
    chan_name_to_curve
[channel_name] = datacurve
    eegplot
.addItem(datacurve)
eegplot
.getAxis('left').setTicks([ticklabels])
eegplot
.setLabel('bottom', 'Time', units='sec')

# connect the signals correctly
for channel_name, curve in chan_name_to_curve.items():
    curve
.sigClicked.connect(changeChannelColor)



[1] http://www.pyqtgraph.org/documentation/graphicsItems/plotitem.html#pyqtgraph.PlotItem.plot


Luke Campagnola

unread,
Jul 28, 2017, 8:33:26 PM7/28/17
to pyqt...@googlegroups.com
On Thu, Jul 27, 2017 at 3:02 PM, Christopher Mullins <christoph...@gmail.com> wrote:

(1) Question is what it sounds like.  The situation is that I'm prototyping an EEG analysis application, and I'm trying to simulate the "Select bad channels" functionality in EEGLAB (a MATLAB GUI for this).  This involves plotting sixty-something data channels together separated vertically, labeling the y-ticks with the name of each channel.  When a user clicks a channel's data, the color should change (see minimal working example below).

The trouble is it's easy to see that a certain channel of data is bad, but sometimes difficult to tell exactly which channel that is ie. the name of the channel. So I'd like to change the color of the tick label as well.  Is there a way to specify just _one_ particular tick and change its color?

There is no way (currently) to specify the color of a single tick. However, you could draw a new line (or anything else) on top. The easiest way to do this is just to add a QGraphicsLineItem into the ViewBox. 

If you expect the x range to change, then you might want the tick to automatically set its x position/size like a tick. VTickGroup is one example of how to do this (but it draws vertical ticks; it's meant for marking event times). You could probably adapt that to draw a horizontal tick instead.

Another option is to draw the tick outside the viewbox, but have it track the vertical position of the ViewBox. Here's a simple example:

import pyqtgraph as pg

class Anchor(pg.ItemGroup):
    """Graphics item that anchors its position to the coordinate system inside
    a view box.
    """
    def __init__(self, view, pos, x=True, y=True):
        pg.ItemGroup.__init__(self)
        self.view = view
        self.anchorPos = pg.Point(pos)
        self.anchorAxes = (x, y)
        view.sigRangeChanged.connect(self.updatePosition)
    
    def updatePosition(self):
        pos = self.pos()
        anchorPos = self.parentItem().mapFromScene(self.view.mapViewToScene(self.anchorPos))
        x, y = self.anchorAxes
        if x:
            pos.setX(anchorPos.x())
        if y:
            pos.setY(anchorPos.y())
        self.setPos(pos)


plt = pg.plot()

# Anchor doesn't draw anything, it just tracks a position in the viewbox
anchor = Anchor(plt.plotItem.vb, [0, 0], x=False, y=True)
anchor.setParentItem(plt.plotItem)

# Attach any shape to the anchor
marker = pg.QtGui.QGraphicsEllipseItem(-10, -10, 20, 20)
marker.setPos(20, 0)
marker.setPen(pg.mkPen('y', width=3))
marker.setParentItem(anchor)


 

Happy for any further suggestions on the code.  Run it by pasting it into a file and running python -i filename.py.

(2) If you look at the comment I've made in my code, I realized that in order for the sigClicked signal to fire, you need to use a PlotCurveItem (as in the MouseSelection.py example).  If you use a PlotDataItem (which is returned from PlotItem.plot() [1]) the function connected to the sigClicked signal won't fire.  Is this expected behavior, or a bug? 

Thanks for the example. If you use a PlotDataItem, you can enable clicking on the curve with `item.curve.setClickable(True)`  (however, in your case a PlotCurveItem is probably sufficient).
However, if you try to use `clickable=True` in the PlotDataItem constructor, it is silently ignored. This is a bug.
 

Luke

Christopher Mullins

unread,
Jul 29, 2017, 12:18:58 PM7/29/17
to pyqtgraph
Thanks Luke! 

I appreciate the suggestions.  Looks like there are plenty of options to solve my problem, even if the individual tick color doesn't change.

I put in a PR for the bugfix [1], so we can discuss the best way to go forward on that thread.  Thanks for this toolkit, it's helped me out a ton.

Chris

[1] #519: https://github.com/pyqtgraph/pyqtgraph/pull/519
Reply all
Reply to author
Forward
0 new messages