Multiple windows and y axis scale

2,366 views
Skip to first unread message

pyqtgraphuser

unread,
Mar 22, 2012, 10:02:53 PM3/22/12
to pyqtgraph
I have a multiple window setup (W1, W2, W3) and there is a region area
displayed in W3. Things work great when I move the region in W3.
Ideally, the scale of the y axis should adjust to reflect the values
needed to display just the data in W1 and W2 as I move the region in
W3. Is there some way to do this?

Luke Campagnola

unread,
Mar 23, 2012, 4:36:30 AM3/23/12
to pyqt...@googlegroups.com
There's nothing built-in that does this, but it's a feature I've wanted myself a few times. It may appear in the future..

For now, I would just compute the desired y-range manually and set it in the same function that handles the region change signal.

Luke

pyqtgraphuser

unread,
Mar 23, 2012, 12:07:53 PM3/23/12
to pyqtgraph

This would be my most requested feature. It will increase usability
tremendously.

On Mar 23, 4:36 am, Luke Campagnola <luke.campagn...@gmail.com> wrote:

pyqtgraphuser

unread,
Apr 15, 2012, 10:45:02 AM4/15/12
to pyqt...@googlegroups.com

For now, I would just compute the desired y-range manually and set it in the same function that handles the region change signal.



I'm trying to get my head around this. The update would need to be done on the following two actions:
( I have multiple plot sub-windows, with the last one having a region that the user can move to update the displayed chart on the other windows). 

The y axis for the last window (with the region box) never gets adjusted 

1. Users moves the region (or changes the size of the region) on the bottom chart. This would affect the displayed data on all the other charts. 
2. Users moves an individual chart left or right (I have the x axis's connected to all but the last chart). Then the scale of the y axis would have to be adjusted for all the charts (except for the last window). 

for 1:

I have an update() function which is connected through: region.sigRegionChanged.connect(update). I would have to iterated through each other chart and set the correct y axis. How would I find out the min and max y values after the region has been applied? ( I can do that myself, since I have the x values and data series, but am wondering if there is an easier way). Secondly, how would I set the y axis scale for each chart after the region has been updated?

for 2:
I connect the y axis as p1.setXLink(p2), so as the users drags the chart on p1, p2 is adjusted correctly? Do I use p1.sigRangeChanged.connect with the update function having the following signature update(view, range) and again how would I get the min and max values after the user move and how would I adjust the y axis scale before the chart is drawn?

Thanks, 

Luke Campagnola

unread,
Apr 16, 2012, 11:31:23 AM4/16/12
to pyqt...@googlegroups.com
On Sun, Apr 15, 2012 at 10:45, pyqtgraphuser <webus...@gmail.com> wrote:

For now, I would just compute the desired y-range manually and set it in the same function that handles the region change signal.



I'm trying to get my head around this. The update would need to be done on the following two actions:
( I have multiple plot sub-windows, with the last one having a region that the user can move to update the displayed chart on the other windows). 

The y axis for the last window (with the region box) never gets adjusted 

1. Users moves the region (or changes the size of the region) on the bottom chart. This would affect the displayed data on all the other charts. 
2. Users moves an individual chart left or right (I have the x axis's connected to all but the last chart). Then the scale of the y axis would have to be adjusted for all the charts (except for the last window). 

for 1:

I have an update() function which is connected through: region.sigRegionChanged.connect(update). I would have to iterated through each other chart and set the correct y axis. How would I find out the min and max y values after the region has been applied? ( I can do that myself, since I have the x values and data series, but am wondering if there is an easier way).

That's right, you'll need to do this computation yourself. Figure out the start and stop indexes, then set the Y range to [yData[start:stop].min(), yData[start:stop].max()]
 
Secondly, how would I set the y axis scale for each chart after the region has been updated?

plot.setYRange(min, max)
 
for 2:
I connect the y axis as p1.setXLink(p2), so as the users drags the chart on p1, p2 is adjusted correctly? Do I use p1.sigRangeChanged.connect with the update function having the following signature update(view, range) and again how would I get the min and max values after the user move and how would I adjust the y axis scale before the chart is drawn?

I don't think it's useful in this case to use setXLink because moving a plot will need to adjust the position of the region item, which should in turn correct the range of the other plot. So #1 looks like:
    Region item moved -> Update plot1 X range, Update plot2 X range, Update both plots Y range
..and #2 looks like:
    Plot1 X range changed -> Update region item -> Update plot2 X range, Update both plots Y range

To avoid infinite loops, you need to make sure that in #2, updating the region item does not cause plot1 X range to be updated again. Also, since you are automatically setting the Y range for all plots, you should disable mouse interaction for that axis with plot.setMouseEnabled(y=False).

pyqtgraphuser

unread,
Apr 17, 2012, 9:00:24 AM4/17/12
to pyqtgraph
> To avoid infinite loops, you need to make sure that in #2, updating the
> region item does not cause plot1 X range to be updated again. Also, since
> you are automatically setting the Y range for all plots, you should disable
> mouse interaction for that axis with plot.setMouseEnabled(y=False).

I can't get this to work. I've spent a significant time on this and
can't seem to find what the problem is.

For #2 -> I disable all update and and update region event handlers, I
set the x range and the region and then enable the event handlers
again. The region window works fine, and dragging in plot 3 works
fine. However if you drag x on plot 1 or plot 2 things go bad. (see
code below)

In the code below, I need to set an updateregion for each plot window
since I need to know which plot window I'm in to calculate the y
range.

---------------------------------------------------------------



import initExample ## Add path to library (just for examples; you do
not need this)
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph.Point import Point
from random import random

#genearte layout
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
p1 = win.addPlot(row=1, col=0)
p2 = win.addPlot(row=2, col=0)
p3 = win.addPlot(row=3, col=0)
p4 = win.addPlot(row=4, col=0)
region = pg.LinearRegionItem()
region.setZValue(10)
p4.addItem(region)

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

p1.plot(data1, pen="r")
p2.plot(data2, pen="g")
p3.plot(data1, pen="w")
p4.plot(data2, pen="w")

for window in [p1, p2, p3]:
window.setMouseEnabled(y=False)

def update():
for i, window in enumerate([p1, p2, p3]):
try:
if (i, "updateRegion") in functionCache:
window.sigRangeChanged.disconnect(functionCache[(i,
"updateRegion")])
except:
print 'Error: '

for window in [p1, p2, p3]:
window.setXRange(*region.getRegion())

for i, window in enumerate([p1, p2, p3]):
try:
if (i, "updateRegion") in functionCache:
window.sigRangeChanged.connect(functionCache[(i,
"updateRegion")])
except:
print 'Error: '

region.sigRegionChanged.connect(update)
region.setRegion([1000, 2000])

functionCache = {}
for i, window in enumerate([p1, p2, p3]):
def updateRegion(view, viewRange):
region.sigRegionChanged.disconnect(update)
for j, window in enumerate([p1, p2, p3]):
try:
if (j, "updateRegion") in functionCache:

window.sigRangeChanged.disconnect(functionCache[(j, "updateRegion")])
except:
print 'Error: '

minX, maxX = viewRange[0]
region.setRegion(viewRange[0])

for j, window in enumerate([p1, p2, p3]):
window.setYRange(10000 - 5000*random(),
10000+5000*random())
if i != j:
window.setXRange(minX, maxX)

for j, window in enumerate([p1, p2, p3]):
try:
if (j, "updateRegion") in functionCache:
window.sigRangeChanged.connect(functionCache[(j,
"updateRegion")])
except:
print 'Error: '

region.sigRegionChanged.connect(update)

functionCache[(i, "updateRegion")] = updateRegion
window.sigRangeChanged.connect(updateRegion)

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

Luke Campagnola

unread,
Apr 17, 2012, 4:40:12 PM4/17/12
to pyqt...@googlegroups.com
On Tue, Apr 17, 2012 at 09:00, pyqtgraphuser <webus...@gmail.com> wrote:
> To avoid infinite loops, you need to make sure that in #2, updating the
> region item does not cause plot1 X range to be updated again. Also, since
> you are automatically setting the Y range for all plots, you should disable
> mouse interaction for that axis with plot.setMouseEnabled(y=False).

I can't get this to work. I've spent a significant time on this and
can't seem to find what the problem is.

I can't quite understand how your script is supposed to work, but I do see an important bug in it: you are defining a function inside a for loop, which almost certainly does not have the effect you have intended. 

For example, I'll attempt to define three functions, each of which prints a different integer:

>>> fns = []                                                                                                                                                                                                                                   
>>> for i in range(3):
...   def fn():
...       print i
...   fns.append(fn)
... 
>>> fns[0]()
2
>>> fns[1]()
2
>>> fns[2]()
2

But you can see the in the end, all three functions print "2". This is because Python is a lexically scoped language, and the variable "i" is the same variable for all three functions.

In any case, see the attached script. I think it has the behavior you are looking for.

test.py

pyqtgraphuser

unread,
Apr 17, 2012, 7:27:59 PM4/17/12
to pyqt...@googlegroups.com

In any case, see the attached script. I think it has the behavior you are looking for.


Great. Your code is much simpler and works. Although I'm still not sure I understand why it doe snot have an infinite loop. 

Drag X on chart 1 -> region gets updated on chart 4 through function updateRegion -> x axis on chart 1 gets set through function update() -> shouldn't  window.sigRangeChanged.connect(updateRegion) catch the change in x axis again and call updateRegion again?

Or the reason is it does not get called is that x axis is set to the same values it already has so there is no sigRangeChanged event. 

Thanks 

Luke Campagnola

unread,
Apr 17, 2012, 8:02:28 PM4/17/12
to pyqt...@googlegroups.com
On Tue, Apr 17, 2012 at 19:27, pyqtgraphuser <webus...@gmail.com> wrote:

In any case, see the attached script. I think it has the behavior you are looking for.


Great. Your code is much simpler and works. Although I'm still not sure I understand why it doe snot have an infinite loop. 

Sorry, I meant to have an explanation in that last message :)
 
Drag X on chart 1 -> region gets updated on chart 4 through function updateRegion -> x axis on chart 1 gets set through function update() -> shouldn't  window.sigRangeChanged.connect(updateRegion) catch the change in x axis again and call updateRegion again?

Or the reason is it does not get called is that x axis is set to the same values it already has so there is no sigRangeChanged event. 

You guessed correctly. Ordinarily there would be an infinite loop and some sort of trickery would be needed to break the loop. To my delight, I found that the loop was already broken for the reason you guessed. Importantly, you must use the padding=0 argument when setting the range on the plot windows. By default, padding=0.02 which means that the displayed region is 2% larger than the region you ask for. By setting padding=0, the region is set to exactly what you request and the loop is broken.

Luke

pyqtgraphuser

unread,
Apr 18, 2012, 7:43:01 AM4/18/12
to pyqtgraph
> windows. By default, padding=0.02 which means that the displayed region is
> 2% larger than the region you ask for. By setting padding=0, the region is
> set to exactly what you request and the loop is broken.

Ok, that was the missing piece. I did not have padding=0 in my last
attempt and I was seeing the infinite loop, which is why I had to add
all the disconnect/connect event code. Now it makes sense and the
overall example is much simpler.

Thanks!

Luke Campagnola

unread,
May 8, 2012, 6:28:21 PM5/8/12
to pyqt...@googlegroups.com
On Mar 23, 4:36 am, Luke Campagnola <luke.campagn...@gmail.com> wrote:
> On Thu, Mar 22, 2012 at 22:02, pyqtgraphuser <webuser1...@gmail.com> wrote:
> > I have a multiple window setup (W1, W2, W3) and there is a region area
> > displayed in W3. Things work great when I move the region in W3.
> > Ideally, the scale of the y axis should adjust to reflect the values
> > needed to display just the data in W1 and W2 as I move the region in
> > W3. Is there some way to do this?
>
> There's nothing built-in that does this, but it's a feature I've wanted
> myself a few times. It may appear in the future..

On Fri, Mar 23, 2012 at 12:07 PM, pyqtgraphuser <webus...@gmail.com> wrote:
This would be my most requested feature. It will increase usability
tremendously.

Wish granted :)
This feature is in the latest revision.

Luke

 

pyqtgraphuser

unread,
May 11, 2012, 7:08:56 AM5/11/12
to pyqt...@googlegroups.com

Wish granted :)
This feature is in the latest revision.


I'll test it out this weekend. It will allow me to remove some of the code I put in place to handle this. 

My next big feature request is support and example on how to do multiple graphs (with two different axis's (one on left and one of right) in the same plot area. 

 

Luke Campagnola

unread,
May 11, 2012, 8:50:06 AM5/11/12
to pyqt...@googlegroups.com
Here's a simple example that demonstrates the basic idea:

import pyqtgraph as pg
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()

p1 = pw.plotItem
p2 = pg.ViewBox()
p1.showAxis('right')
p1.scene().addItem(p2)
p2.setGeometry(p1.vb.sceneBoundingRect())
p1.getAxis('right').linkToView(p2)
p2.setXLink(p1)

p1.plot([1,4,2,3,6,4])
p2.addItem(p1.plot([10,40,20,30,60,40], pen='b'))

It has some basic problems--only one of the viewbox context menus can be accessed, the second viewbox does not respond to resize events, etc. but all these things could be addressed easily. I'd like to build this in to PlotItem, but I'm not quite sure what the API should look like yet. Any suggestions? Maybe something like this:

plot.addView(axes='right')  # creates second view, links the right axis to it
plot.plot(data1, view=0)
plot.plot(data2, view=1)

Luke







 

-- [ You are subscribed to pyqt...@googlegroups.com. To unsubscribe, send email to pyqtgraph+...@googlegroups.com ]

pyqtgraphuser

unread,
Oct 3, 2012, 8:34:26 PM10/3/12
to pyqt...@googlegroups.com
Do you know how to use the auto scale of y axis feature when the x axis of multiple graphs are looked together. 

Luke Campagnola

unread,
Oct 3, 2012, 8:46:15 PM10/3/12
to pyqt...@googlegroups.com
On Wed, Oct 3, 2012 at 8:34 PM, pyqtgraphuser <webus...@gmail.com> wrote:
Do you know how to use the auto scale of y axis feature when the x axis of multiple graphs are looked together. 

Not sure I understand why linking on one axis one would interfere with scaling on the other. 
You can enable auto-scaling for just the Y axis through the context menu (rt. click -> Y Axis -> Auto) or by calling  plot.enableAutoRange(axis='y')
 
Luke

pyqtgraphuser

unread,
Oct 3, 2012, 8:55:29 PM10/3/12
to pyqt...@googlegroups.com
Was not sure if the x axis link is an issue or not. But when I do:

for i in range(1, 4)
        plot = win.addPlot(row=i, col=0)
        plot.enableAutoRange(axis='y')
        
It does not auto scale the y axis. What am I doing wrong? I'm not on the latest release though

pyqtgraphuser

unread,
Oct 3, 2012, 8:59:28 PM10/3/12
to pyqt...@googlegroups.com
I'm on revision 178. 

Luke Campagnola

unread,
Oct 3, 2012, 9:01:36 PM10/3/12
to pyqt...@googlegroups.com
On Wed, Oct 3, 2012 at 8:55 PM, pyqtgraphuser <webus...@gmail.com> wrote:
Was not sure if the x axis link is an issue or not. But when I do:

for i in range(1, 4)
        plot = win.addPlot(row=i, col=0)
        plot.enableAutoRange(axis='y') 
        
It does not auto scale the y axis. What am I doing wrong? I'm not on the latest release though


I'll need to see a little more context.. plots are set to auto-scale by default, so there shouldn't even be any need to set that parameter immediately after creating the plot.

There have not been any significant changes to ViewBox since rev 178.
 

pyqtgraphuser

unread,
Oct 3, 2012, 9:15:44 PM10/3/12
to pyqt...@googlegroups.com
Hmmm. I've just upgraded to 201 and still the same problem. Do you know how I can debug this? What file should I look at?

Thanks,

Luke Campagnola

unread,
Oct 3, 2012, 9:35:21 PM10/3/12
to pyqt...@googlegroups.com
On Wed, Oct 3, 2012 at 9:15 PM, pyqtgraphuser <webus...@gmail.com> wrote:
Hmmm. I've just upgraded to 201 and still the same problem. Do you know how I can debug this? What file should I look at?

I still don't understand what the problem is. Can you send a working code sample demonstrating the issue? 
 

pyqtgraphuser

unread,
Oct 3, 2012, 9:44:47 PM10/3/12
to pyqt...@googlegroups.com
When I run the crosshair.py example the y axis does not change as I move the selected region on the bottom plot. Same problem when I make the selected region really small. 

Luke Campagnola

unread,
Oct 3, 2012, 10:04:44 PM10/3/12
to pyqt...@googlegroups.com
On Wed, Oct 3, 2012 at 9:44 PM, pyqtgraphuser <webus...@gmail.com> wrote:
When I run the crosshair.py example the y axis does not change as I move the selected region on the bottom plot. Same problem when I make the selected region really small. 

By default, the plot will auto-scale to fit the entire data set. If you want just the visible portion of the data, try rt. click -> Y Axis -> Visible Data Only
 

pyqtgraphuser

unread,
Oct 3, 2012, 10:44:10 PM10/3/12
to pyqt...@googlegroups.com
That would explain it. Is there any way to set this through code?

Luke Campagnola

unread,
Oct 4, 2012, 6:56:12 AM10/4/12
to pyqt...@googlegroups.com
On Wednesday, October 3, 2012 10:05:05 PM UTC-4, Luke Campagnola wrote:
By default, the plot will auto-scale to fit the entire data set. If you want just the visible portion of the data, try rt. click -> Y Axis -> Visible Data Only

On Wed, Oct 3, 2012 at 10:44 PM, pyqtgraphuser <webus...@gmail.com> wrote:
That would explain it. Is there any way to set this through code?

The method is ViewBox.setAutoVisible(y=True), and it is available through PlotItem as well.  (I see I neglected to document this method)

pyqtgraphuser

unread,
Oct 4, 2012, 9:52:13 PM10/4/12
to pyqt...@googlegroups.com
When I do that I get the following error messages:

    plot.setAutoVisible(y=True)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 523, in setAutoVisible
    self.updateAutoRange()
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 580, in updateAutoRange
    self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 402, in setRange
    self.updateAutoRange()
  
  ...
  <Lots of the same error messages >
  ...
  
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 580, in updateAutoRange
    self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 402, in setRange
    self.updateAutoRange()
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 580, in updateAutoRange
    self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 402, in setRange
    self.updateAutoRange()
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 580, in updateAutoRange
    self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 394, in setRange
    self.updateMatrix(changed)
  File "pyqtgraph/graphicsItems/ViewBox/ViewBox.py", line 1111, in updateMatrix
    scale = scale * Point(1, -1)
  File "pyqtgraph/Point.py", line 75, in __mul__
    return self._math_('__mul__', a)
  File "pyqtgraph/Point.py", line 97, in _math_
    x = Point(x)
  File "pyqtgraph/Point.py", line 30, in __init__
    QtCore.QPointF.__init__(self, float(args[0][0]), float(args[0][1]))
RuntimeError: maximum recursion depth exceeded

Luke Campagnola

unread,
Oct 5, 2012, 8:46:42 AM10/5/12
to pyqt...@googlegroups.com
I'm not able to reproduce this, even with linked plots. Can you give me some example code to demonstrate the bug?

Luke


-- [ You are subscribed to pyqt...@googlegroups.com. To unsubscribe, send email to pyqtgraph+...@googlegroups.com ]

pyqtgraphuser

unread,
Oct 6, 2012, 10:34:22 AM10/6/12
to pyqt...@googlegroups.com
Ok figured out the problem. You will get this exception if you enable this before writing to the plot. Everything is ok if you enable this after you have finished writing your data to the plot area. 

However, I have one other problem. if there is a pg.ArrowItem on the plot the the y axis does not get updated. 

Luke Campagnola

unread,
Oct 6, 2012, 1:49:32 PM10/6/12
to pyqt...@googlegroups.com
On Sat, Oct 6, 2012 at 10:34 AM, pyqtgraphuser <webus...@gmail.com> wrote:
Ok figured out the problem. You will get this exception if you enable this before writing to the plot. Everything is ok if you enable this after you have finished writing your data to the plot area. 

Sorry, still not able to reproduce, but I guess you've got it working anyway  (still, I'd love to fix the bug if you can send me a simple example)
 
However, I have one other problem. if there is a pg.ArrowItem on the plot the the y axis does not get updated. 

Got this one; thanks. I'll try to post a fix soon.


Luke 

pyqtgraphuser

unread,
Oct 6, 2012, 4:39:50 PM10/6/12
to pyqt...@googlegroups.com
Sorry, still not able to reproduce, but I guess you've got it working anyway  (still, I'd love to fix the bug if you can send me a simple example)


Here you go. I've updated the cross hair example to show the problem.  
crosshair.py

Luke Campagnola

unread,
Oct 7, 2012, 12:52:38 AM10/7/12
to pyqt...@googlegroups.com
On Sat, Oct 6, 2012 at 4:39 PM, pyqtgraphuser <webus...@gmail.com> wrote:
Sorry, still not able to reproduce, but I guess you've got it working anyway  (still, I'd love to fix the bug if you can send me a simple example)

Here you go. I've updated the cross hair example to show the problem.  

Got it; thanks very much! 
I'll post a fix tomorrow after I've done some more testing.

Luke



Reply all
Reply to author
Forward
0 new messages