Add axes labels and title to PyQtGraph ImageView - python

I use a PyQtGraph ImageView to display multi-dimensional data.
What is the easiest way to add axes labels and a title to such an ImageView?
I tried adding a LabelItem to the underlying ViewBox. I assume positioning it correctly requires hacking the underlying layout. Is this the way to go, or is there an easier way?
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
app = QtGui.QApplication([])
# make ImageView with test data
imv = pg.ImageView()
data = np.fromfunction(lambda i, j: np.sin(i/16)*j/128, (512, 512), dtype=float) \
+ np.random.normal(scale=0.2, size=(512, 512))
imv.setImage(data)
# add label
vbox = imv.getView()
vbox.addItem(pg.LabelItem("this is a nice label"))
imv.show()
app.exec_()

import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
app = QtGui.QApplication([])
# Add Plot item to show axis labels
plot = pg.PlotItem()
plot.setLabel(axis='left', text='Y-axis')
plot.setLabel(axis='bottom', text='X-axis')
# make ImageView with test data
imv = pg.ImageView(view=plot) # set the plot to ImageView's view
data = np.fromfunction(lambda i, j: np.sin(i/16)*j/128, (512, 512), dtype=float) + np.random.normal(scale=0.2, size=(512, 512))
imv.setImage(data)
# add label
vbox = imv.getView()
vbox.addItem(pg.LabelItem("this is a nice label"))
imv.show()
app.exec_()

Related

Why can't I successfully draw the region of interest in a matplotlib color map embedded in a pyqt5 gui?

I'm trying to draw a region of interest on a color map that is embedded in a pyqt5 gui. This is an example of what I want.
import sys
from PyQt5.QtWidgets import (QWidget, QPushButton,
QHBoxLayout, QVBoxLayout, QApplication)
from PyQt5 import QtCore
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import ROI_class as roi # ROI_class.py
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.drawButton.clicked.connect(self.draw_map_Callback)
self.roiButton.clicked.connect(self.choose_roi)
def initUI(self):
self.drawButton = QPushButton("draw map")
self.roiButton = QPushButton("roi")
self.hbox = QHBoxLayout()
self.hbox.addStretch(1)
self.hbox.addWidget(self.drawButton)
self.hbox.addWidget(self.roiButton)
self.vbox = QVBoxLayout()
self.vbox.addStretch(1)
self.vbox.addLayout(self.hbox)
self.setLayout(self.vbox)
self.setGeometry(500, 500, 500, 500)
self.setWindowTitle('ROI')
self.show()
def draw_map_Callback(self):
img = np.ones((100, 100)) * range(0, 100)
fig, ax1 = plt.subplots()
self.con_canvas = FigureCanvas(plt.figure(tight_layout=True))
self.con_canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
self.con_canvas.setFocus()
self.con_toolbar = NavigationToolbar(self.con_canvas, self)
self.vbox.addWidget(self.con_toolbar)
self.vbox.addWidget(self.con_canvas)
self._con_ax = self.con_canvas.figure.subplots()
self.con_img = self._con_ax.imshow(img, cmap ='jet')
self._con_ax.set_xlabel('xlabel')
self._con_ax.set_ylabel('ylabel')
self.con_cbar = self.con_canvas.figure.colorbar(self.con_img)
self._con_ax.set_aspect('equal')
def choose_roi(self):
y = roi.new_ROI(self.con_img)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
It will draw a colormap when I click "draw map". Then I want it to allow me to draw a region of interest with my mouse and get a mask using the code on this link below.
https://github.com/martindurant/misc/blob/master/ROI.py
The "ROI_class" that is imported is just a copy and paste of the code in the link above.
I can successfully draw the plot on the GUI but when I click "roi", it doesn't allow me to draw the region of interest.
When I mad a new file and paste the code in the link above with something like
fig, ax1 = plt.subplots()
s = ax1.imshow(img, cmap ='jet')
ax1.set_xlabel('subcolor')
ax1.set_ylabel('ylabel')
y = new_ROI(s)
at the end of the code, it worked just fine and I was able to draw the region of interest and get the mask of it.
But when I try to do this in the GUI, it wouldn't let me draw the region of interest. I'm very confused why this isn't working.
The problem is that picker (the variable "y") is a local variable that gets destroyed instantly causing the desired behavior not to be executed. The solution is to make it an attribute of the class:
self.y = roi.new_ROI(self.con_img)

Put a Matplotlib plot as a QGraphicsItem/into a QGraphicsView

So I have a very basic plot layout described below (with x and y values changed for brevity):
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
figure = Figure()
axes = figure.gca()
axes.set_title(‘My Plot’)
x=np.linspace(1,10)
y=np.linspace(1,10)
y1=np.linspace(11,20)
axes.plot(x,y,’-k’,label=‘first one’)
axes.plot(x,y1,’-b’,label=‘second one’)
axes.legend()
axes.grid(True)
And I have designed a GUI in QT designer that has a GraphicsView (named graphicsView_Plot) that I would like to put this graph into and I would like to know how I would go about putting this graph into the GraphicsView. Barring starting over and using the QT based graphing ability I don’t really know how (if possible) to put a matplotlib plot into this graphics view. I know it would be a super simple thing if I can convert it into a QGraphicsItem as well, so either directly putting it into the GraphicsView or converting it to a QGraphicsItem would work for me.
You have to use a canvas that is a QWidget that renders the matplotlib instructions, and then add it to the scene using addWidget() method (or through a QGraphicsProxyWidget):
import sys
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
figure = Figure()
axes = figure.gca()
axes.set_title("My Plot")
x = np.linspace(1, 10)
y = np.linspace(1, 10)
y1 = np.linspace(11, 20)
axes.plot(x, y, "-k", label="first one")
axes.plot(x, y1, "-b", label="second one")
axes.legend()
axes.grid(True)
canvas = FigureCanvas(figure)
proxy_widget = scene.addWidget(canvas)
# or
# proxy_widget = QtWidgets.QGraphicsProxyWidget()
# proxy_widget.setWidget(canvas)
# scene.addItem(proxy_widget)
view.resize(640, 480)
view.show()
sys.exit(app.exec_())

How to show matplotlib.pyplot in qt widget?

I want to show the pyplot image in widget (QWidget) that I put in a gui designed in QtDesigner:
When I push the Çiz button I want to show the image that I can create in python with that code:
points = np.array([(1, 1), (2, 4), (3, 1), (9, 3)])
x = points[:,0]
y = points[:,1]
# calculate polynomial
z = np.polyfit(x, y, 2)
f = np.poly1d(z)
x_fit = np.linspace(min(x), max(x), 10000)
y_fit = [f(_x) for _x in x_fit]
plt.plot(x, y)
plt.plot(x_fit, y_fit)
plt.show()
EDIT
I made some changes according to the answer but I have new problems.
After I promote it:
I rearrange my code below:
# calculate polynomial and r
self.x_fit = np.linspace(min(self.itemX), max(self.itemY), 10000)
self.y_fit = [f(_x) for _x in self.x_fit]
self._dlg.plotwidget.plot(self.itemX, self.itemY)
self._dlg.plotwidget.plot(self.x_fit, self.y_fit)
self.itemX is x values in a list.
self.itemY is y values in a list.
self._dlg is the MainWindow that you see.
When I try to open that window I get this error message:
I just tested the code below works for me:
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, uic
uifilename = 'test.ui'
form_class = uic.loadUiType(uifilename)[0] #dirty reading of the ui file. better to convert it to a '.py'
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
self.onInit()
def onInit(self):
#usually a lot of connections here
self.x_fit = np.linspace(1,10000, 10000)
self.y_fit = [f(_x) for _x in self.x_fit]
self.plotwidget.plot(self.x_fit,self.y_fit,symbol='o',pen=None)
self.plotwidget.setLabel('left',text='toto',units='')
self.plotwidget.setLabel('top',text='tata',units='')
def f(x):
return x**2+1
if __name__ == '__main__':
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
app = QtGui.QApplication([])
pg.setConfigOption('background', 'w')
win = MyWindowClass()
win.show()
app.exec_()
with this test.ui : https://github.com/steph2016/profiles/blob/master/test.ui.
Note the plotwidget is encapsulated inside a layout. I thought it works with just the Main Window, but I'm not sure anymore...
Sorry I don't exactly answer the question but I don't use matplotlib.pyplot with pyqt. I recommend using pyqtgraph (http://pyqtgraph.org), which is quite convenient and powerful. With Qt Designer:
you just insert a 'graphics view' in your GUI
you right-click on it and promote it to "plotwidget" using pyqtgraph. I guess the english tab is something like this: base class 'QGraphicsView'; promoted class 'PlotWidget'; header file 'pyqtgraph', then 'add', then 'promote'
then you can make plotwidget.plot(x,y,...), plotwidget.clear(), and co. It will plot everything inside the plotwidget with a bunch of interacting possibilities

Widget is appearing next to image?

I am attempting to put pyqtgraph ROI widgets (information here) on top of a .PNG image. When I import the image into the program, it comes out rotated and flipped the wrong way. I assume this is a bug. To attempt to fix it, I have rotated the image BUT when I do so, my ROI widget goes off of the image. How do I fix this?
Without image rotation:
i = Image.open("del.png")
a = array(i) #converting to numpy array
img1a = pg.ImageItem(a)
v1a.addItem(img1a)
Once I add img1a.rotate(90) to the code above, the ROI widget goes off the screen. How do I position the image the correct way and have my ROI widget appear normally on top of the image?
The entire code is found below (edited from this example found here.)
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
from numpy import array
from PIL import Image
## create GUI
app = QtGui.QApplication([])
w = pg.GraphicsWindow(size=(1000,800), border=True)
w.setWindowTitle('pyqtgraph example: ROI Examples')
text = """text"""
w1 = w.addLayout(row=0, col=0)
label1 = w1.addLabel(text, row=0, col=0)
v1a = w1.addViewBox(row=1, col=0, lockAspect=True)
#img1a = pg.ImageItem(arr)
i = Image.open("del.png")
a = array(i)
img1a = pg.ImageItem(a)
v1a.addItem(img1a)
img1a.rotate(90)
v1a.disableAutoRange('xy')
v1a.autoRange()
rois = []
rois.append(pg.EllipseROI([150, 150], [1, 1], pen=(4,9)))
rois.append(pg.EllipseROI([0, 0], [300, 300], pen=(4,9)))
for roi in rois:
v1a.addItem(roi)
## 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_()
You do not have to rotate the item, but you must rotate the image for it you can use numpy.rot90:
i = Image.open("del.png")
a = array(i)
a = np.rot90(a, -1)
img1a = pg.ImageItem(a)
v1a.addItem(img1a)
v1a.disableAutoRange('xy')
v1a.autoRange()

turning grid on with AxisItem in pyqtgraph causes axis scaling to break

I am having trouble with AxisItem. As soon as I turn on both the x and y grid, the x-axis is no longer able to scale in and out with the zoom/pan function. Any ideas?
from PyQt4 import QtCore, QtGui
from pyqtgraph import Point
import pyqtgraph as pg
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
class plotClass(QtGui.QMainWindow):
def setupUi(self, MainWindow):
self.centralwidget = QtGui.QWidget(MainWindow)
MainWindow.resize(1900, 1000)
self.viewbox = pg.GraphicsView(MainWindow, useOpenGL=None, background='default')
self.viewbox.setGeometry(QtCore.QRect(0, 0, 1600, 1000))
self.layout = pg.GraphicsLayout()
self.viewbox.setCentralWidget(self.layout)
self.viewbox.show()
self.view = self.layout.addViewBox()
self.axis1 = pg.AxisItem('bottom', linkView=self.view, parent=self.layout)
self.axis2 = pg.AxisItem('right', linkView=self.view, parent=self.layout)
self.axis1.setGrid(255)
self.axis2.setGrid(255)
self.layout.addItem(self.axis1, row=1, col=0)
self.layout.addItem(self.axis2, row=0, col=1)
if __name__== "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = plotClass()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Looking at your last comment, consider this option:
The pyqtgraph examples folder contains a "GraphItem.py" example which adds and displays a GraphItem object to a window via a ViewBox only. They don't use a grid however, so if you want to use a grid with a GraphItem, just add a PlotItem first (which has an associated ViewBox already... and you guessed it,...AxisItems for a grid!),... then get the ViewBox to add your GraphItems. The modified GraphItem.py would look like this (with the accompanying showGrid):
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
# Enable antialiasing for prettier plots
pg.setConfigOptions(antialias=True)
w = pg.GraphicsWindow()
w.setWindowTitle('pyqtgraph example: GraphItem')
### comment out their add of the viewbox
### since the PlotItem we're adding will have it's
### own ViewBox
#v = w.addViewBox()
pItem1 = w.addPlot() # this is our new PlotItem
v = pItem1.getViewBox() # get the PlotItem's ViewBox
v.setAspectLocked() # same as before
g = pg.GraphItem() # same as before
v.addItem(g) # same as before
pItem1.showGrid(x=True,y=True) # now we can turn on the grid
### remaining code is the same as their example
## Define positions of nodes
pos = np.array([
[0,0],
[10,0],
[0,10],
[10,10],
[5,5],
[15,5]
])
## Define the set of connections in the graph
adj = np.array([
[0,1],
[1,3],
[3,2],
[2,0],
[1,5],
[3,5],
])
## Define the symbol to use for each node (this is optional)
symbols = ['o','o','o','o','t','+']
## Define the line style for each connection (this is optional)
lines = np.array([
(255,0,0,255,1),
(255,0,255,255,2),
(255,0,255,255,3),
(255,255,0,255,2),
(255,0,0,255,1),
(255,255,255,255,4),
], dtype=[('red',np.ubyte),('green',np.ubyte),('blue',np.ubyte),('alpha',np.ubyte),('width',float)])
## Update the graph
g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False)
## 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_()
I tested this and the scroll/zooming still worked after enabling the grid, so still not sure why doing it the other way DOESN'T work, but sometimes finding another way is the best answer :)

Categories