I'm using pyqtgraph to plot tracks of a robot (the path that the bot drove). Now I want to add a marker to the plot to indicate the bots current position and heading. I thought ArrowItem would be the right choice, because it is scale invariant and can be rotated easily. However the local origin of the arrow is at its tip like this
but I want it to be in the center like this
How can I do that? I would also appreciate different solutions to this problem.
Update
After applying eyllansec's code I get some rendering problems. A minimal example (one has to zoom or move the view to disable the auto scaling):
import pyqtgraph as pg
import numpy as np
import time
class CenteredArrowItem(pg.ArrowItem):
def paint(self, p, *args):
p.translate(-self.boundingRect().center())
pg.ArrowItem.paint(self, p, *args)
if __name__ == '__main__':
app = pg.QtGui.QApplication([])
window = pg.GraphicsWindow(size=(1280, 720))
window.setAntialiasing(True)
tracker = window.addPlot(title='Tracker')
while True:
for i in range(300):
arrow = CenteredArrowItem(angle=i, headLen=40, tipAngle=45, baseAngle=30)
arrow.setPos(i / 300, i / 300)
tracker.addItem(arrow)
app.processEvents()
time.sleep(0.02)
tracker.removeItem(arrow)
As you may noticed I'm adding and removing the arrow each iteration. This is because arrow.setStyle(angle=i) is not working as it does not update the rotation of the arrow (probably a bug).
A possible solution is to overwrite the paint method of ArrowItem and move the QPainter:
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
class MyArrowItem(pg.ArrowItem):
def paint(self, p, *args):
p.translate(-self.boundingRect().center())
pg.ArrowItem.paint(self, p, *args)
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
p = pg.PlotWidget()
p.showGrid(x = True, y = True, alpha = 0.3)
w.show()
w.resize(640, 480)
w.setCentralWidget(p)
w.setWindowTitle('pyqtgraph example: Arrow')
a = pg.ArrowItem(angle=-160, tipAngle=60, headLen=40, tailLen=40, tailWidth=20, pen={'color': 'w', 'width': 3}, brush='r')
b = MyArrowItem(angle=-160, tipAngle=60, headLen=40, tailLen=40, tailWidth=20, pen={'color': 'w', 'width': 3})
a.setPos(10,0)
b.setPos(10,0)
p.addItem(a)
p.addItem(b)
## 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_()
As shown in the following figure, the red arrow is the default ArrowItem, and the blue is the offset, both are located in the same position with respect to the plot.
Update:
The problem is caused by the method that rotates the item used as the center of coordinates using the center of transformations by default, that is to say the (0, 0), we must move it:
import pyqtgraph as pg
import numpy as np
import time
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph import functions as fn
class CenteredArrowItem(pg.ArrowItem):
def setStyle(self, **opts):
# http://www.pyqtgraph.org/documentation/_modules/pyqtgraph/graphicsItems/ArrowItem.html#ArrowItem.setStyle
self.opts.update(opts)
opt = dict([(k,self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']])
tr = QtGui.QTransform()
path = fn.makeArrowPath(**opt)
tr.rotate(self.opts['angle'])
p = -path.boundingRect().center()
tr.translate(p.x(), p.y())
self.path = tr.map(path)
self.setPath(self.path)
self.setPen(fn.mkPen(self.opts['pen']))
self.setBrush(fn.mkBrush(self.opts['brush']))
if self.opts['pxMode']:
self.setFlags(self.flags() | self.ItemIgnoresTransformations)
else:
self.setFlags(self.flags() & ~self.ItemIgnoresTransformations)
if __name__ == '__main__':
app = pg.QtGui.QApplication([])
window = pg.GraphicsWindow(size=(1280, 720))
window.setAntialiasing(True)
tracker = window.addPlot(title='Tracker')
while True:
for i in range(300):
arrow = CenteredArrowItem(angle=i, headLen=40, tipAngle=45, baseAngle=30)
arrow.setPos(i / 300, i / 300)
tracker.addItem(arrow)
app.processEvents()
time.sleep(0.02)
tracker.removeItem(arrow)
After digging through the source code of pyqtgraph I ended up with a special function that suits my needs. I apply the translation when creating the arrow path, instead when rendering it. Fortunately this also solves the roation bug (for whatever reason).
Example:
import pyqtgraph as pg
import numpy as np
import time
import pyqtgraph.functions
class CenteredArrowItem(pg.ArrowItem):
def setData(self, x, y, angle):
self.opts['angle'] = angle
opt = dict([(k, self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']])
path = pg.functions.makeArrowPath(**opt)
b = path.boundingRect()
tr = pg.QtGui.QTransform()
tr.rotate(angle)
tr.translate(-b.x() - b.width() / 2, -b.y() - b.height() / 2)
self.path = tr.map(path)
self.setPath(self.path)
self.setPos(x, y)
if __name__ == '__main__':
app = pg.QtGui.QApplication([])
window = pg.GraphicsWindow(size=(1280, 720))
window.setAntialiasing(True)
tracker = window.addPlot(title='Tracker')
arrow = CenteredArrowItem(headLen=40, tipAngle=45, baseAngle=30)
tracker.addItem(arrow)
tracker.addItem(pg.InfiniteLine(pos=(0,0), angle=45))
center = pg.ScatterPlotItem([], [], brush='r')
tracker.addItem(center)
while True:
for i in range(300):
arrow.setData(i, i, i)
center.setData([i], [i])
app.processEvents()
time.sleep(0.02)
Related
I'd like to plot the horizontal distance between 2 points on an image with PyQtGraph, but I can't draw it.
I think it a way of doing this would be to use 3 instances of LineSegmentROI and make them look connected as one right arc, because they already have many features that would be great for this idea.
Like being draggable, which could be very useful to measure a different distance by simply dragging a side.
The problem are the handles, that can't be removed, or even hidden.
Has anyone done something like this?
# import the necessary packages
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import requests
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
image = cv2.imread('example.png') # Change if you save the image with a different name
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
app = QtGui.QApplication([])
## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.resize(image.shape[0], image.shape[1]) # Depending on the picture you may need to resize
w.setWindowTitle('Test')
view = pg.ViewBox()
view.setLimits(xMin=-image.shape[0]*0.05, xMax=image.shape[0]*1.05,
minXRange=100, maxXRange=2000,
yMin=-image.shape[1]*0.05, yMax=image.shape[1]*1.05,
minYRange=100, maxYRange=2000)
w.setCentralItem(view)
## lock the aspect ratio
view.setAspectLocked(True)
## Add image item
item = ImageItem(image)
view.addItem(item)
# Add Line
line = pg.QtGui.QGraphicsLineItem(200, -100, 400, -100, view)
line.setPen(pg.mkPen(color=(255, 0, 0), width=10))
view.addItem(line)
def mouseClicked(evt):
pos = evt[0]
print(pos)
proxyClicked = pg.SignalProxy(w.scene().sigMouseClicked, rateLimit=60, slot=mouseClicked)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I ended up borrowing RectItem from drawing a rectangle in pyqtgraph and using its code for LineItem.
With three LineItems I draw the distance meter between the 2 points.
I still have to add some signals and slots to handle resizing, but I'm working on it.
However the core of the solution is here and I'll follow up with my improvements
# import the necessary packages
from PySide2.QtCore import QLineF, Qt, Signal, Slot, QObject, QPointF, QRectF, QSizeF
from PySide2.QtGui import QRegion
from PySide2.QtWidgets import QGraphicsItem, QLabel, QWidget
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from pyqtgraph.graphicsItems.ViewBox.ViewBox import ViewBox
image = cv2.imread('image.jpg')
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
app = QtGui.QApplication([])
## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.setWindowTitle('Test')
view = pg.ViewBox()
view.setLimits(xMin=0, xMax=image.shape[0],
minXRange=100, maxXRange=2000,
yMin=0, yMax=image.shape[1],
minYRange=100, maxYRange=2000)
w.setCentralItem(view)
## lock the aspect ratio
view.setAspectLocked(True)
## Add image item
item = ImageItem(image)
view.addItem(item)
class LineItem(pg.UIGraphicsItem):
moved = Signal(QPointF)
def __init__(self, line, extend=0, horizontal=False, parent=None):
super().__init__(parent)
self.initialPos = QLineF(line)
self._line = line
self.extend = extend
self.horizontal = horizontal
self._extendLine()
self.picture = QtGui.QPicture()
self._generate_picture()
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
#property
def line(self):
return self._line
def _extendLine(self):
if (self.extend != 0 and not self.horizontal):
self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
# if (self.horizontal):
# self.extend = 0
# self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
def _generate_picture(self):
painter = QtGui.QPainter(self.picture)
painter.setPen(pg.mkPen(color="y", width=2))
painter.drawLine(self.line)
painter.end()
def paint(self, painter, option, widget=None):
painter.drawPicture(0, 0, self.picture)
def boundingRect(self):
lineShape = self.picture.boundingRect()
lineShape.adjust(-10, -10, 10, 10)
return QtCore.QRectF(lineShape)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange:
# value is the new position.
if self.horizontal:
if value.x() != 0:
value = QPointF(0, value.y())
else:
if value.y() != 0:
value = QPointF(value.x(), 0)
self.moved.emit(value)
return pg.UIGraphicsItem.itemChange(self, change, value)
class Distance(QObject):
def __init__(self, A: QPointF, B: QPointF, view: ViewBox, parent: QWidget=None):
super().__init__(parent)
self.A = A
self.B = B
if A.x() > B.x():
self.A, self.B = B, A
self.distance = abs(B.x() - A.x())
print(self.A)
print(self.B)
extend = 50
top = max(self.A.y(), self.B.y()) + 200
self.left = LineItem(QtCore.QLineF(self.A.x(), self.A.y(), self.A.x(), top), extend)
self.right = LineItem(QtCore.QLineF(self.B.x(), self.B.y(), self.B.x(), top), extend)
self.top = LineItem(QtCore.QLineF(self.A.x(), top, self.B.x(), top), horizontal=True)
self.top.setPos(0, 0)
self.left.moved.connect(self.onLeftSegmentMoved)
self.right.moved.connect(self.onRightSegmentMoved)
self.top.moved.connect(self.onTopSegmentMoved)
self.label = pg.TextItem(str(round(self.distance, 2)), color=(0xFF, 0xFF, 0x00), anchor=(1, 1))
# self.label.setParentItem(self.top)
self.label.setPos(self.A.x()+self.distance/2, top + 5)
view.addItem(self.label)
view.addItem(self.left)
view.addItem(self.top)
view.addItem(self.right)
#Slot(QPointF)
def onLeftSegmentMoved(self, delta: QPointF):
topLeft = self.top.initialPos.p1()
newX = topLeft.x() + delta.x()
newTopLeft = QPointF(newX, topLeft.y())
self.top.line.setP1(newTopLeft)
self.top._generate_picture()
pos = self.label.pos()
self.distance = abs(self.top.line.x2() - self.top.line.x1())
self.label.setPos(newX + (self.top.line.x2() - self.top.line.x1())/2, pos.y())
self.label.setText(str(round(self.distance, 2)))
#Slot(QPointF)
def onTopSegmentMoved(self, delta: QPointF):
leftTop = self.top.initialPos.p1()
newY = leftTop.y() + delta.y()
newLeftTop = QPointF(leftTop.x(), newY)
self.left.line.setP2(newLeftTop)
self.left._generate_picture()
rightTop = self.top.initialPos.p2()
newY = rightTop.y() + delta.y()
newRightTop = QPointF(rightTop.x(), newY)
self.right.line.setP2(newRightTop)
self.right._generate_picture()
pos = self.label.pos()
self.label.setPos(pos.x(), newY)
#Slot(QPointF)
def onRightSegmentMoved(self, delta: QPointF):
topRight = self.top.initialPos.p2()
newX = topRight.x() + delta.x()
newTopRight = QPointF(newX, topRight.y())
self.top.line.setP2(newTopRight)
self.top._generate_picture()
pos = self.label.pos()
self.distance = abs(self.top.line.x2() - self.top.line.x1())
self.label.setPos(newX - (self.top.line.x2() - self.top.line.x1())/2, pos.y())
self.label.setText(str(round(self.distance, 2)))
distance = Distance(QPointF(925, 425), QPointF(138, 500), view)
I use pyqtgraph to draw the candles, but if I zoom graph very much, image hide.
I noticed that when zoom very much, “paint” method stops being called. Code with http request to exchange below.
image example
import pyqtgraph as pg
from pyqtgraph import QtCore, QtGui
from PyQt5 import QtWidgets
class CandlestickItem(QtWidgets.QGraphicsRectItem):
def __init__(self, data):
super(CandlestickItem, self).__init__()
self.data = data
self.generatePicture()
def generatePicture(self):
self.picture = QtGui.QPicture()
p = QtGui.QPainter(self.picture)
p.setPen(pg.mkPen('w'))
w = (self.data[1][0] - self.data[0][0]) / 3.
for (t, open, close, min, max) in self.data:
if max != min:
p.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max))
if open > close:
p.setBrush(pg.mkBrush('r'))
else:
p.setBrush(pg.mkBrush('g'))
p.drawRect(QtCore.QRectF(t - w, open, w * 2, close - open))
p.end()
def paint(self, p, *args):
print('paint call')
p.drawPicture(0, 0, self.picture)
def boundingRect(self):
return QtCore.QRectF(self.picture.boundingRect())
if __name__ == '__main__':
import sys
import urllib.request
import json
get_symbols_url = 'https://api.hitbtc.com/api/2/public/candles/BCNBTC?period=M30&limit=1000'
response = urllib.request.urlopen(get_symbols_url)
request_data = json.loads(response.read())
data_list = []
for i in range(0, len(request_data)):
data_list.append((float(i), float(request_data[i]['open']), float(request_data[i]['close']),
float(request_data[i]['min']), float(request_data[i]['max'])))
item = CandlestickItem(data_list)
plt = pg.plot()
plt.addItem(item)
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I need to display very small values, for example, 0.0000001-0.00000015. What can I do to prevent image hidden at high zoom?
I was answered on the support forum and it works for me.
question on forum
It seems to be a rounding error. Need replace method of boundingRect.
# import numpy as np
def boundingRect(self):
data = np.array(self.data)
xmin = data[:,0].min()
xmax = data[:,0].max()
ymin = data[:,1:].min()
ymax = data[:,1:].max()
return QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
I want to synchronize the X-Axis of several pyqtgraph plots. When the user rescales the X-Axis with mouse interactions (e.g. scroll-wheel while mouse on x-Axis) I want to assign the same changes to all the other plots. So how do I do this?
I derived a minimized code from a basic example below.
Do I have to overwrite the viewRangeChanged() functions of w1 and w2?
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.console
import numpy as np
from pyqtgraph.dockarea import *
win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.resize(1000,500)
win.setWindowTitle('pyqtgraph example: dockarea')
d1 = Dock("Dock1")
d2 = Dock("Dock2")
area.addDock(d1, 'bottom')
area.addDock(d2, 'bottom', d1)
w1 = pg.PlotWidget(title="Dock 1 plot")
w1.plot(np.random.normal(size=100))
d1.addWidget(w1)
w2 = pg.PlotWidget(title="Dock 2 plot")
w2.plot(np.random.normal(size=100))
d2.addWidget(w2)
win.show()
## 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_()
This question has a follow up here with another answer to this question.
We need to use the sigRangeChanged signal and connect it to a slot, the problem is that the change of the range another item would generate the signal sigRangeChanged and so on generating an infinite loop, to solve this you must disconnect those signals before making the modifications and reconnect them to the final.
w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)
def onSigRangeChanged(r):
w1.sigRangeChanged.disconnect(onSigRangeChanged)
w2.sigRangeChanged.disconnect(onSigRangeChanged)
if w1 == r:
w2.setRange(xRange=r.getAxis('bottom').range)
elif w2 == r:
w1.setRange(xRange=r.getAxis('bottom').range)
w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)
Example:
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
from pyqtgraph.dockarea import *
import sys
def onSigRangeChanged(r):
w1.sigRangeChanged.disconnect(onSigRangeChanged)
w2.sigRangeChanged.disconnect(onSigRangeChanged)
if w1==r:
w2.setRange(xRange=r.getAxis('bottom').range)
elif w2 == r:
w1.setRange(xRange=r.getAxis('bottom').range)
w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)
app = QtGui.QApplication(sys.argv)
win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.resize(1000,500)
win.setWindowTitle('pyqtgraph example: dockarea')
d1 = Dock("Dock1")
d2 = Dock("Dock2")
area.addDock(d1, 'bottom')
area.addDock(d2, 'bottom', d1)
w1 = pg.PlotWidget(title="Dock 1 plot")
it=w1.plot(np.random.normal(size=100))
d1.addWidget(w1)
w2 = pg.PlotWidget(title="Dock 2 plot")
w2.plot(np.random.normal(size=100))
d2.addWidget(w2)
w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)
win.show()
sys.exit(app.exec_())
Better yet,
Instead of disconnecting then reconnecting signals, it is possible to use blockSignals.
here is a generic way to synchronize any number of plots :
syncedPlots = [w1, w2, w3] # put as many plots as you wish
def onSigRangeChanged(r):
for g in syncedPlots:
if g !=r :
g.blockSignals(True)
g.setRange(xRange=r.getAxis('bottom').range)
g.blockSignals(False)
for g in syncedPlots:
g.sigRangeChanged.connect(onSigRangeChanged)
There is a better answer in this question:
Instead of connecting to the sigRangeChanged event we can directly link the axes
scales by w2.setXLink(w1).
I'm using pyqtgraph to plot graphics where I put some text (for example the max of the data...)
My question is how to know the size (height, width) of a TextItem input? It's for controling its place for avoiding being out of the window.
For example my code could be this :
# -*- coding: utf-8 -*-
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
pg.setConfigOptions(antialias=True)
voieTT=np.ones(100)
voieTemps=np.ones(100)
for i in range(100):
voieTT[i]=np.random.random_sample()
voieTemps[i]=i
signemax=np.max(voieTT)
tmax=np.where(voieTT==np.max(voieTT))[0][0]
grapheTT = win.addPlot(enableMenu=False)
grapheTTVB=grapheTT.getViewBox()
grapheTT.plot(voieTemps, voieTT)
grapheTT.plot([tmax],[signemax],symbol='o',symbolSize=8)
grapheTT.showGrid(x=True, y=True)
textVmax = pg.TextItem(anchor=(0.5,1.5), border='w', fill=(0,0,255))
textVmax.setHtml('<div style="text-align: center"><span style="color: #FFF;">Vmax=%0.1f mm/s # %0.1f s</span></div>'%(np.abs(signemax),tmax))
grapheTT.addItem(textVmax)
textVmax.setPos(tmax, signemax)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
#changed original code to simple plot() because manual window structure
#somehow didn't automatically cooperate with viewbox' map methods
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
voieTT=np.ones(100)
voieTemps=np.ones(100)
for i in range(100):
voieTT[i]=np.random.random_sample()
voieTemps[i]=i
signemax=np.max(voieTT)
tmax=np.where(voieTT==np.max(voieTT))[0][0]
grapheTT = pg.plot(enableMenu=False)
grapheTTVB=grapheTT.getViewBox()
grapheTT.plot(voieTemps, voieTT)
grapheTT.plot([tmax],[signemax],symbol='o',symbolSize=8)
grapheTT.showGrid(x=True, y=True)
textVmax = pg.TextItem(anchor=(0.5,1.5), border='w', fill=(0,0,255))
textVmax.setHtml('<div style="text-align: center"><span style="color: #FFF;">Vmax=%0.1f mm/s # %0.1f s</span></div>'%(np.abs(signemax),tmax))
grapheTT.addItem(textVmax)
textVmax.setPos(tmax, signemax)
#DISCLAIMER: there's likely a better way, but this works too:
#you can get pixel dimensions x,y,w,h from bounding rectangle
br = textVmax.boundingRect()
print br
#and scale them into viewbox data dimensions
sx, sy = grapheTTVB.viewPixelSize() #scaling factors
print sx, sy
print sx*br.width(), sy*br.height()
#ALSO, you can just use ViewBox autorange
#ViewBox autoranges based on its contained items' dataBounds() info
#however TextItem.py is a UIGraphicsItem.py with dataBounds() return None
"""Called by ViewBox for determining the auto-range bounds.
By default, UIGraphicsItems are excluded from autoRange."""
#so, following example of... ArrowItem.py
#here is a simple dataBounds() for TextItem
def Autoranging_TextItem_dataBounds(axis, frac=1.0, orthoRange=None):
br = textVmax.boundingRect()
sx, sy = grapheTTVB.viewPixelSize()
w, h = sx*br.width(), sy*br.height()
if axis == 0: return [-w/2, w/2]
if axis == 1: return [0, h]
textVmax.dataBounds = Autoranging_TextItem_dataBounds
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Here is an attempt of doing a waterfall representation.
I need to express the signal(array) values(amplitudes/levels/densities) in different colors not in shades as done.
As I'm an algorithmic and signal processing eng. and not a software developer, I'm not familiar with the color maps and these stuff. So if someone could hep me out with the code for relating the colors with the signal values.
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import scipy.ndimage as ndi
import numpy as np
Nf = 90 # No. of frames
Ns = 100 # Signal length
app = QtGui.QApplication([])
w_SA = QtGui.QWidget(); w_SA.setFixedSize(400, 400)
# Create a GL View widget to display data
plt_SA1 = gl.GLViewWidget(w_SA); plt_SA1.move(10, 10); plt_SA1.resize(380, 380)
plt_SA1.setCameraPosition(elevation=90.0, azimuth=0.0, distance=70)
p1 = gl.GLSurfacePlotItem(shader='shaded', color=(0.5, 0.5, 1, 1), smooth=False)
p1.translate(-Nf/2, -Ns/2, 0)
plt_SA1.addItem(p1)
Arx = np.zeros([Nf, Ns])
def update():
global Arx
Arx = np.roll(Arx, 1, axis=0)
Arx[0] = ndi.gaussian_filter(np.random.normal(size=(1,Ns)), (1,1))
p1.setData(z=Arx)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(30)
w_SA.show()
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Can you be more specific about how you want the image to be colored? If you don't need OpenGL, here is a simpler solution using pyqtgraph.ImageView. You can right-click on the gradient bar on the right side to change the lookup table used to color the image. There are also a variety of ways to set this table manually, depending on the desired effect.
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import scipy.ndimage as ndi
import numpy as np
Nf = 90 # No. of frames
Ns = 100 # Signal length
app = QtGui.QApplication([])
Arx = np.zeros([Nf, Ns])
win = pg.image(Arx)
win.view.setAspectLocked()
def update():
global Arx
Arx = np.roll(Arx, 1, axis=0)
Arx[0] = ndi.gaussian_filter(np.random.normal(size=(1,Ns)), (1, 1))
win.setImage(Arx.T, autoRange=False)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(30)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()