I have plotted a 3D graph with pyqtgraph, and I want to save / export it.
A right mouse click on the 3D plot doesn't open any context menu which would allow me to save the plot.
The doc at http://www.pyqtgraph.org/documentation/exporting.html tells me how to save / export from within a program but following the instructions for 3D results in a black saved image.
Here is the relevant portion of my code:
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
import pyqtgraph.exporters
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setCameraPosition(distance=50)
g = gl.GLGridItem()
g.scale(2,2,1)
g.setDepthValue(10)
w.addItem(g)
z=np.genfromtxt('../mydata.txt')
p1 = gl.GLSurfacePlotItem(z=z, shader='shaded', color=(0.5, 0.5, 1, 1))
p1.scale(0.1, 0.1, 0.1)
p1.translate(-0, 0, 0)
w.addItem(p1)
w.grabFrameBuffer().save('test.png')
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
mydata.txt contains a 2D array of float values. The plot appears correctly on my screen.
Has anyone been successful in saving / exporting a 3D plot from a pyqtgraph program or is able to spot an issue in the code above? (Linux, Using Qt version 4.8.7 in anaconda3).
This is the relevant information which helped me to solve my problem:
https://groups.google.com/forum/#!msg/pyqtgraph/dKT1Z3nIeow/OErAgRPAbB8J
That is:
d = w.renderToArray((1000, 1000))
pg.makeQImage(d).save(filename)
And here below is the full code which creates a 3D plot and saves it:
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
import pyqtgraph.exporters
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setCameraPosition(distance=50)
g = gl.GLGridItem()
g.scale(2,2,1)
g.setDepthValue(10)
w.addItem(g)
z=np.genfromtxt('../../TestData/textAsImage.txt')
p1 = gl.GLSurfacePlotItem(z=z, shader='shaded', color=(0.5, 0.5, 1, 1))
p1.scale(0.1, 0.1, 0.1)
p1.translate(-0, 0, 0)
w.addItem(p1)
filename = 'yourfilename.png'
d = w.renderToArray((1000, 1000))
pg.makeQImage(d).save(filename)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Related
I'm trying to plot a numpy array using QScatterSeries, however only the axis are updated and the points are not displayed. I'm not sure why it is not working.
projectionwindow.py
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QWidget, QHBoxLayout
from PySide2.QtGui import QColor, QPen
from PySide2.QtCharts import QtCharts
class ProjectionWindow(QWidget):
"""
TODO
"""
def __init__(self, parent=None) -> 'None':
super().__init__()
self.setWindowTitle('Projection')
self.resize(800, 800)
self.chart = QtCharts.QChart()
self.chart_view = QtCharts.QChartView(self.chart)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.chart_view)
self.setLayout(self.layout)
self.show()
def loadCharts(self, data: 'ndarray') -> 'None':
points = QtCharts.QScatterSeries()
points.setMarkerSize(2.0)
for i in range(data.shape[0]):
points.append(data[i, 0], data[i, 1])
self.chart.addSeries(points)
self.chart.createDefaultAxes()
self.chart.show()
This is my current result when calling
main.py
import sys
import numpy as np
from PySide2.QtWidgets import QApplication
from ui.projectionwindow import ProjectionWindow
if __name__ == "__main__":
app = QApplication(sys.argv)
data = np.array([[1,2],
[3,4]])
window = ProjectionWindow(app)
window.loadCharts(data)
sys.exit(app.exec_())
Resulted obtained:
You have 2 errors:
The markerSize is very small that makes it indistinguishable to the eye.
When establishing a series for the first time, the QChart takes the minimum rectangle so that in your case it is in the corners, so the solution is to change the minimum and maximum value of the axes considering an adequate margin.
def loadCharts(self, data: "ndarray") -> "None":
points = QtCharts.QScatterSeries()
points.setMarkerSize(20)
for i in range(data.shape[0]):
points.append(data[i, 0], data[i, 1])
self.chart.addSeries(points)
self.chart.createDefaultAxes()
m_x, M_x = min(data[:, 0]), max(data[:, 0])
m_y, M_y = min(data[:, 1]), max(data[:, 1])
ax = self.chart.axes(Qt.Horizontal, points)[0]
ax.setMin(m_x - 1)
ax.setMax(M_x + 1)
ay = self.chart.axes(Qt.Vertical, points)[0]
ay.setMin(m_y - 1)
ay.setMax(M_y + 1)
Output:
I'm try to implement a crosshair item on a imageview using pyqtgraph, it seems ok with the functions.
But, when I zoom in the image and move the crosshair, there are some broken points or lines on the boundary of pixel.
Any suggestions will be much appreciated.
completed code as below, could be run and replicate the problem.
# -*- coding: utf-8 -*-
"""
This example demonstrates the use of ImageView, which is a high-level widget for
displaying and analyzing 2D and 3D data. ImageView provides:
1. A zoomable region (ViewBox) for displaying the image
2. A combination histogram and gradient editor (HistogramLUTItem) for
controlling the visual appearance of the image
3. A timeline for selecting the currently displayed frame (for 3D data only).
4. Tools for very basic analysis of image data (see ROI and Norm buttons)
"""
## Add path to library (just for examples; you do not need this)
#import initExample
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import *
# Interpret image data as row-major instead of col-major
pg.setConfigOptions(imageAxisOrder='row-major')
app = QtGui.QApplication([])
## Create window with ImageView widget
win = QtGui.QMainWindow()
win.resize(800,800)
plt=pg.PlotItem()
imv = pg.ImageView(view=plt)
label = pg.LabelItem(justify='right')
plt.addItem(label)
win.setCentralWidget(imv)
win.show()
win.setWindowTitle('pyqtgraph example: ImageView')
## Create random 3D data set with noisy signals
img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
img = img[np.newaxis,:,:]
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
data = np.random.normal(size=(100, 200, 200))
data += img * decay
data += 2
## Add time-varying signal
sig = np.zeros(data.shape[0])
sig[30:] += np.exp(-np.linspace(1,10, 70))
sig[40:] += np.exp(-np.linspace(1,10, 60))
sig[70:] += np.exp(-np.linspace(1,10, 30))
sig = sig[:,np.newaxis,np.newaxis] * 3
data[:,50:60,30:40] += sig
## Display the data and assign each frame a time value from 1.0 to 3.0
imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))
## Set a custom color map
colors = [
(0, 0, 0),
(45, 5, 61),
(84, 42, 55),
(150, 87, 60),
(208, 171, 141),
(255, 255, 255)
]
cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors)
imv.setColorMap(cmap)
#cross hair
vLine = pg.InfiniteLine(angle=90, movable=False)
hLine = pg.InfiniteLine(angle=0, movable=False)
plt.addItem(vLine, ignoreBounds=True)
plt.addItem(hLine, ignoreBounds=True)
vb = plt.vb
def mouseMoved(evt):
pos = evt[0] ## using signal proxy turns original arguments into a tuple
if plt.sceneBoundingRect().contains(pos):
mousePoint = vb.mapSceneToView(pos)
# index = int(mousePoint.x())
# if index > 0 and index < len(data):
# label.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>" % (mousePoint.x(), data[index, index]))
vLine.setPos(mousePoint.x())
hLine.setPos(mousePoint.y())
proxy = pg.SignalProxy(plt.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved)
## 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 expect the image should be good at anytime, when I move the crosshair.
Thank you.
When I change the scale of the axis of my image, my ScaleBar shows the incorrect scale. How do I update the scale bar when I change the axes?
from PyQt5 import QtWidgets
import pyqtgraph as pg
import numpy as np
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
imvOCTTopLeft = pg.ImageView(view=pg.PlotItem())
imvOCTTopLeft.setImage(np.random.normal(size=(100,100)))
imvOCTTopLeft.view.getAxis('left').setScale(0.6)
imvOCTTopLeft.view.getAxis('bottom').setScale(0.4)
scale = pg.ScaleBar(size=10,suffix = "px")
viewbox = imvOCTTopLeft.view
if not isinstance(viewbox, pg.ViewBox): viewbox = viewbox.getViewBox()
scale.setParentItem(viewbox)
scale.anchor((1, 1), (1, 1), offset=(-20, -20))
imvOCTTopLeft.show()
sys.exit(app.exec_())
This image shows that the scale bar is showing approximately 4 pixels but states that it is showing 10 pixels.
I think this is because I changed the axis scale.
This seems to be a bug: link. The viewbox rescales after sigRangeChanged is emitted.
"Hacky" solution is to delay the ScaleBar update:
(You might need to play around with the time, 100 and 10 worked for me. If it doesnt work, increase it.)
from PyQt5 import QtWidgets, QtCore
import pyqtgraph as pg
import numpy as np
def updateDelay(scale, time):
QtCore.QTimer.singleShot(time, scale.updateBar)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
plotItem = pg.PlotItem()
imvOCTTopLeft = pg.ImageView(view=plotItem)
imvOCTTopLeft.setImage(np.random.normal(size=(100, 100)))
imvOCTTopLeft.view.getAxis('left').setScale(0.6)
scale = 0.4 #edit
imvOCTTopLeft.view.getAxis('bottom').setScale(scale) #edit
scale = pg.ScaleBar(size=10*(1/scale), suffix="px") #edit
scale.text.setText('10 px') #edit
plotItem.sigRangeChanged.connect(lambda: updateDelay(scale, 10)) # here: time=10ms
viewbox = imvOCTTopLeft.view
if not isinstance(viewbox, pg.ViewBox): viewbox = viewbox.getViewBox()
scale.setParentItem(viewbox)
scale.anchor((1, 1), (1, 1), offset=(-20, -20))
imvOCTTopLeft.show()
updateDelay(scale, 100) # here time=100ms
sys.exit(app.exec_())
Result:
My goal is to create stacked 3d bar plot, for that, I am trying to change color of GlBarGraphItem from PYQTgraph library.
Here is my code:
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 100
w.showMaximized()
w.setWindowTitle('pyqtgraph example: GLViewWidget')
ax = gl.GLAxisItem()
ax.setSize(20,20,20)
w.addItem(ax)
pos = np.mgrid[0:1,0:1,0:1].reshape(3,1,1).transpose(1,2,0)
size = np.empty((1,1,3))
size[...,0:2] = 1
size[...,2] = 5
bg = gl.GLBarGraphItem(pos, size)
##bg.setColor(1., 1., 1., 1.)
w.addItem(bg)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I tried to use .setColor() method with no success, the object (GlBarGraphItem) itself does not have a method for setting color I believe.
Any hint how to progress?
If the code of the GLMeshItem paint() method is revised:
if self.colors is None:
color = self.opts['color']
if isinstance(color, QtGui.QColor):
glColor4f(*fn.glColor(color))
else:
glColor4f(*color)
so the setColor() function expects a QColor or a tuple of 4 elements so you can use the following methods:
bg.setColor((0., 1., 0., 1))
color = QtGui.QColor("pink")
bg.setColor(color)
color = QtGui.QColor(120, 14, 12)
bg.setColor(color)
I have to plot a grid of subplots (16x16 ones, for example). I use matplotlib but It is a bit too slow, especially when I have to redraw the plots (for example, when their size is changed). How can I make it faster?
P.S. Here is an example of code:
import sys
import matplotlib
matplotlib.use('Qt4Agg')
import pylab
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import PyQt4
from PyQt4 import QtCore, QtGui, Qt
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# generate the plot
fig = Figure(figsize=(800, 800), facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
for i in range(256):
ax = fig.add_subplot(16, 16, i)
ax.plot([0, 1])
ax.set_xticks([])
ax.set_yticks([])
# ax.set_title("Mega %i" % (i,))
# generate the canvas to display the plot
canvas = FigureCanvas(fig)
canvas.setMinimumWidth(640)
canvas.setMinimumHeight(640)
# generate layout
layout = QtGui.QVBoxLayout();
layout.addWidget(canvas)
layout.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
# generate widget
widget = QtGui.QWidget()
widget.setLayout(layout)
# generate scroll area
win = QtGui.QScrollArea()
win.setWidget(widget)
win.setMinimumWidth(100)
win.setWidgetResizable(True)
win.show()
canvas.draw()
sys.exit(app.exec_())
I don't have an environment to test your code but I these steps work for me:
Using cProfile to profile the whole thing (f.e. How can you profile a python script?).
Usually its one or two functions which slow down all.
Search stackoverflow/the internet for these function names. Usually 1-2 people solved the performance issue already.
Greetings Kuishi