diff --git a/examples/__main__.py b/examples/__main__.py index 628b93fd..95c0ebae 100644 --- a/examples/__main__.py +++ b/examples/__main__.py @@ -19,6 +19,7 @@ examples = OrderedDict([ ('Scatter Plot', 'ScatterPlot.py'), ('Text Item', 'text.py'), ('ViewBox', 'ViewBox.py'), + ('Linked Views', 'linkedViews.py'), ('Arrow', 'Arrow.py'), ])), ('Widgets', OrderedDict([ diff --git a/examples/linkedViews.py b/examples/linkedViews.py new file mode 100644 index 00000000..c1777aab --- /dev/null +++ b/examples/linkedViews.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +## This example demonstrates the ability to link the axes of views together +## Views can be linked manually using the context menu, but only if they are given names. + + +import initExample ## Add path to library (just for examples; you do not need this) + + +from pyqtgraph.Qt import QtGui, QtCore +import numpy as np +import pyqtgraph as pg + +#QtGui.QApplication.setGraphicsSystem('raster') +app = QtGui.QApplication([]) +#mw = QtGui.QMainWindow() +#mw.resize(800,800) + +x = np.linspace(-50, 50, 1000) +y = np.sin(x) / x + +win = pg.GraphicsWindow(title="View Linking Examples") +win.resize(800,600) + +win.addLabel("Views linked at runtime:", colspan=2) +win.nextRow() + +p1 = win.addPlot(x=x, y=y, name="Plot1", title="Plot1") +p2 = win.addPlot(x=x, y=y, name="Plot2", title="Plot2 - Y linked with Plot1") +p2.setLabel('bottom', "Label to test offset") +p2.setYLink(p1) + +win.nextRow() + +p3 = win.addPlot(x=x, y=y, name="Plot3", title="Plot3 - X linked with Plot1") +p4 = win.addPlot(x=x, y=y, name="Plot4", title="Plot4 - X and Y linked with Plot1") +p3.setLabel('left', "Label to test offset") +QtGui.QApplication.processEvents() +p3.setXLink(p1) +p4.setXLink(p1) +p4.setYLink(p1) + + +## 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_() + diff --git a/graphicsItems/GraphicsLayout.py b/graphicsItems/GraphicsLayout.py index c1d28c28..9bd9f5f9 100644 --- a/graphicsItems/GraphicsLayout.py +++ b/graphicsItems/GraphicsLayout.py @@ -33,7 +33,6 @@ class GraphicsLayout(GraphicsWidget): return self.currentCol-colspan def addPlot(self, row=None, col=None, rowspan=1, colspan=1, **kargs): - from PlotItem import PlotItem plot = PlotItem(**kargs) self.addItem(plot, row, col, rowspan, colspan) return plot @@ -43,6 +42,11 @@ class GraphicsLayout(GraphicsWidget): self.addItem(vb, row, col, rowspan, colspan) return vb + def addLabel(self, text, row=None, col=None, rowspan=1, colspan=1, **kargs): + text = LabelItem(text, **kargs) + self.addItem(text, row, col, rowspan, colspan) + return text + def addItem(self, item, row=None, col=None, rowspan=1, colspan=1): if row is None: @@ -95,3 +99,4 @@ class GraphicsLayout(GraphicsWidget): ## Must be imported at the end to avoid cyclic-dependency hell: from ViewBox import ViewBox from PlotItem import PlotItem +from LabelItem import LabelItem \ No newline at end of file diff --git a/graphicsItems/LabelItem.py b/graphicsItems/LabelItem.py index c9d88dd6..6a2f6b48 100644 --- a/graphicsItems/LabelItem.py +++ b/graphicsItems/LabelItem.py @@ -10,12 +10,11 @@ class LabelItem(GraphicsWidget): GraphicsWidget displaying text. Used mainly as axis labels, titles, etc. - Note: To display text inside a scaled view (ViewBox, PlotWidget, etc) use QGraphicsTextItem - with the flag ItemIgnoresTransformations set. + Note: To display text inside a scaled view (ViewBox, PlotWidget, etc) use TextItem """ - def __init__(self, text, parent=None, **args): + def __init__(self, text, parent=None, angle=0, **args): GraphicsWidget.__init__(self, parent) self.item = QtGui.QGraphicsTextItem(self) self.opts = args @@ -26,6 +25,7 @@ class LabelItem(GraphicsWidget): self.opts['color'] = fn.colorStr(args['color'])[:6] self.sizeHint = {} self.setText(text) + self.setAngle(angle) def setAttr(self, attr, value): diff --git a/graphicsItems/ViewBox/ViewBox.py b/graphicsItems/ViewBox/ViewBox.py index 0bc84c2c..2bddf2fe 100644 --- a/graphicsItems/ViewBox/ViewBox.py +++ b/graphicsItems/ViewBox/ViewBox.py @@ -480,11 +480,13 @@ class ViewBox(GraphicsWidget): self.linksBlocked = b ## prevents recursive plot-change propagation def linkedXChanged(self): + ## called when x range of linked view has changed view = self.state['linkedViews'][0] self.linkedViewChanged(view, ViewBox.XAxis) def linkedYChanged(self): - view = self.state['linkedViews'][0] + ## called when y range of linked view has changed + view = self.state['linkedViews'][1] self.linkedViewChanged(view, ViewBox.YAxis) @@ -502,15 +504,27 @@ class ViewBox(GraphicsWidget): view.blockLink(True) try: if axis == ViewBox.XAxis: - upp = float(vr.width()) / vg.width() - x1 = vr.left() + (sg.x()-vg.x()) * upp - x2 = x1 + sg.width() * upp + overlap = min(sg.right(), vg.right()) - max(sg.left(), vg.left()) + if overlap < min(vg.width()/3, sg.width()/3): ## if less than 1/3 of views overlap, + ## then just replicate the view + x1 = vr.left() + x2 = vr.right() + else: ## views overlap; line them up + upp = float(vr.width()) / vg.width() + x1 = vr.left() + (sg.x()-vg.x()) * upp + x2 = x1 + sg.width() * upp self.enableAutoRange(ViewBox.XAxis, False) self.setXRange(x1, x2, padding=0) else: - upp = float(vr.height()) / vg.height() - x1 = vr.bottom() + (sg.y()-vg.y()) * upp - x2 = x1 + sg.height() * upp + overlap = min(sg.bottom(), vg.bottom()) - max(sg.top(), vg.top()) + if overlap < min(vg.height()/3, sg.height()/3): ## if less than 1/3 of views overlap, + ## then just replicate the view + x1 = vr.top() + x2 = vr.bottom() + else: ## views overlap; line them up + upp = float(vr.height()) / vg.height() + x1 = vr.top() + (sg.y()-vg.y()) * upp + x2 = x1 + sg.height() * upp self.enableAutoRange(ViewBox.YAxis, False) self.setYRange(x1, x2, padding=0) finally: diff --git a/widgets/GraphicsLayoutWidget.py b/widgets/GraphicsLayoutWidget.py index 937c880a..287fbbb9 100644 --- a/widgets/GraphicsLayoutWidget.py +++ b/widgets/GraphicsLayoutWidget.py @@ -7,6 +7,6 @@ class GraphicsLayoutWidget(GraphicsView): def __init__(self, parent=None, **kargs): GraphicsView.__init__(self, parent) self.ci = GraphicsLayout(**kargs) - for n in ['nextRow', 'nextCol', 'addPlot', 'addViewBox', 'addItem', 'getItem']: + for n in ['nextRow', 'nextCol', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLabel']: setattr(self, n, getattr(self.ci, n)) self.setCentralItem(self.ci)