I have a matplotlib image embedded in PyQt:
But am now having trouble updating it.
The UI and the initial embedding is set up as follows:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# Ui_MainWindow is a python class converted from .ui file
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.imageDisp = ImageDisplay() # Class definition below
layoutImage = self.Image_Area.layout() # Image_Area is a Qt Group Box
if layoutImage is None:
layoutImage = QVBoxLayout(self.Image_Area)
ax_img = self.imageDisp.displayImage()
canvas_image = FigureCanvas(ax_img.figure)
layoutImage.addWidget(canvas_image)
self.NextImageButton.clicked.connect(self.inc)
def inc(self):
self.imageDisp.nextImage()
layoutImage = self.Image_Area.layout()
if layoutImage is None:
layoutImage = QVBoxLayout(self.Image_Area)
ax_img = self.imageDisp.UpdateImage()
canvas_image = FigureCanvas(ax_img.figure)
self.imageDisp.draw()
layoutImage.addWidget(canvas_image)
And the ImageDisplay class is defined as follows:
class ImageDisplay(FigureCanvas): # FigureCanvas is matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg
def __init__(self):
# Other attributes
fig = Figure()
self.fig = fig
self.axes = fig.add_subplot(111)
def nextImage(self):
# Do something here
self.UpdateImage()
def UpdateImage(self):
self.axes.imshow(np.asarray(IMAGE))
return self.axes
def displayImage(self):
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
self.axes.imshow(np.asarray(IMAGE))
return self.axes
This does not update the image, but addWidget would create a new widget on top of it, resulting in a stacked plot:
I tried to find ways to remove the widget but to no avail so far. Is there a way I can properly remove this widget and make a new one for the updated image? Or is there another way to display and update an image embedded in PyQt?
try this instead
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
import numpy as np
from matplotlib import image
import matplotlib.pyplot as plt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.imageDisp = ImageDisplay(self, width=5, height=4, dpi=100)
self.imageDisp.displayImage()
self.NextImageButton=QtWidgets.QPushButton('next image',self)
self.NextImageButton.clicked.connect(self.inc)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.imageDisp)
layout.addWidget(self.NextImageButton)
widget = QtWidgets.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.show()
def inc(self):
self.imageDisp.nextImage()
class ImageDisplay(FigureCanvas): # FigureCanvas is matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.start=0
self.files=['ball.png','image.jpg']
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.fig.clear()
self.axes = self.fig.add_subplot(111)
self.axes.clear()
self.axes.axis('off')
plt.draw()
super(ImageDisplay, self).__init__(self.fig)
def nextImage(self):
# Do something here
self.IMAGE = image.imread(self.files[self.start%2])
self.UpdateImage()
def UpdateImage(self):
self.start+=1
self.axes.clear()
self.axes.axis('off')
self.axes.imshow(np.asarray(self.IMAGE))
self.draw()
def displayImage(self):
self.IMAGE = image.imread(self.files[self.start%2])
self.start+=1
self.axes.clear()
self.axes.axis('off')
self.axes.imshow(np.asarray(self.IMAGE))
self.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
sys.exit(app.exec_())
Related
In the below example, I update a figure with varying signal length. the relim function works find except when I zoom in the figure. If I zoomed in, the relim doesn't reset the correct limit and the axes stay zoomed in, whatever the length of the signal I plot. I don't know what I'm doing wrong here.
from PyQt5.QtWidgets import *
import sys
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.setFixedWidth(600)
self.setFixedHeight(600)
self.layout = QVBoxLayout()
self.button = QPushButton('redraw')
self.myscene= Viewer_signal(self)
self.layout.addWidget(self.button)
self.layout.addWidget(self.myscene)
self.centralWidget.setLayout(self.layout)
self.button.clicked.connect(self.load)
def load(self):
self.myscene.update_Signal(np.random.rand(np.random.randint(1000,100000)),1000)
class Viewer_signal(QGraphicsView):
def __init__(self, parent=None):
super(Viewer_signal, self).__init__(parent)
self.parent=parent
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.figure = Figure(facecolor='white')
self.figure.subplots_adjust(left=0.08, bottom=0.03, right=0.98, top=0.99, wspace=0.28 , hspace=0.30)
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
self.axes_Sig = self.figure.add_subplot(111)
self.axes_Sig.set_xlabel("Time [s]")
self.axes_Sig.set_ylabel("Amp [mV]")
self.Line_,= self.axes_Sig.plot(0,0 )
self.canvas.setGeometry(0, 0, 1500, 500)
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
self.figure.tight_layout()
def update_Signal(self,data,Fs):
t = np.arange(data.shape[0])/Fs
self.Line_.set_data(t, data)
self.axes_Sig.relim()
self.axes_Sig.autoscale_view(True,True,True)
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
update
even setting the limits by and doesn't help:
def update_Signal(self,data,Fs):
t = np.arange(data.shape[0])/Fs
self.Line_.set_data(t, data)
miniy = min(data)
maxiy = max(data)
self.axes_Sig.update_datalim( ((0, miniy), (t[-1], maxiy)))
self.axes_Sig.relim()
self.axes_Sig.autoscale_view(True,True,True)
self.canvas.draw()
I think you want to use autoscale() instead of autoscale_view()
def update_Signal(self,data,Fs):
t = np.arange(data.shape[0])/Fs
self.Line_.set_data(t, data)
self.axes_Sig.relim()
self.axes_Sig.autoscale()
self.canvas.draw()
I wrote the following code in python to show a bar chart in the GUI generated by PyQt5.
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout, \
QLineEdit, QMessageBox, QInputDialog, QLabel, QHBoxLayout, QGridLayout, QStackedLayout, QFormLayout
from PyQt5 import QtCore, QtGui, QtWidgets
import time
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.lines import Line2D
import matplotlib.animation as animation
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
# data
import numpy as np
#import matplotlib.pyplot as plt
import pandas as pd
import os
import csv
#define the path to data
pathToData='C:/RC/python/data/'
dataName= '31122017ARB.csv'
# import
data=pd.read_csv(pathToData+dataName, delimiter=';',encoding='cp1252')
# percent MW
data['MWP']=data[' MW ']/sum(data[' MW '])
#aggregate by Best
datag=data.groupby('Best', as_index=False).agg({'MW': 'sum'})
x=["BE"+s for s in[str(s) for s in [int(x) for x in datag.iloc[:,0].tolist()]]]
y=datag.ix[:,1].tolist()
figure = plt.figure()
H = np.array([[100, 2, 39, 190], [402, 55, 369, 1023], [300, 700, 8, 412], [170, 530, 330, 1]])
Z = np.array([[3, 290, 600, 480], [1011, 230, 830, 0], [152, 750, 5, 919], [340, 7, 543, 812]])
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init_
self.im = None
self.canvas = FigureCanvas(self.figure)
self.canvas.mpl_connect('button_press_event', self.on_button_press_event)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.plot)
self.timer.setInterval(5000)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.timer.start)
self.button.setDefault(False)
self.stop = QPushButton("Stop")
self.stop.clicked.connect(self.timer.stop)
self.stop.setDefault(False)
self.exit = QPushButton('Exit')
self.exit.clicked.connect(self.close)
self.exit.setDefault(True)
layout = QFormLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
layout.addWidget(self.stop)
layout.addWidget(self.exit)
self.setLayout(layout)
self.lb = QtWidgets.QLabel(self)
self.lb.setWindowFlags(QtCore.Qt.ToolTip)
def plot(self):
self.x = ["BE"+s for s in[str(s) for s in [int(x) for x in datag.iloc[:,0].tolist()]]]
self.y = datag.iloc[:,1].tolist()#np.sin(self.x)
self.setWindowTitle("Bestandseingruppierung")
def update_figure(self):
self.axes.bar(self.x, self.y)
#self.y = np.roll(self.y,-1)
#self.draw()
self.canvas.draw()
def on_button_press_event(self, event):
print('button={}, x={}, y={}, xdata={}, ydata={}'
.format(event.button, event.x, event.y, event.xdata, event.ydata))
if self.im:
message = str(self.im.get_cursor_data(event))
delay = 1000
w = self.lb.fontMetrics().width(message)
self.lb.resize(w, self.lb.size().height())
self.lb.setText(message)
self.lb.move(QtGui.QCursor.pos())
self.lb.show()
QtCore.QTimer.singleShot(delay, self.lb.hide)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
The Problem is: it is not shown any plot, even I do not get any error. I just obtain an empty .
How can I get the chart by pressing the plot bottom? I guess, I am doing some thing wrong under def plot(self):.
Just because of clarification: I tested the chart within a GUI generated by PyQt5 as a stand alone code, which works totally fine:
# FigureCanvas inherits QWidget
class MainWindow(FigureCanvas):
def __init__(self, parent=None, width=4, height=3, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
self.axes.hold(False)
super(MainWindow, self).__init__(fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
timer = QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(50)
self.x = ["BE"+s for s in[str(s) for s in [int(x) for x in datag.iloc[:,0].tolist()]]]#np.arange(0, 4*np.pi, 0.1)
self.y = datag.iloc[:,1].tolist()#np.sin(self.x)
self.setWindowTitle("Best")
def update_figure(self):
self.axes.bar(self.x, self.y)
#self.y = np.roll(self.y,-1)
self.draw()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
I found the solution!
I have 3 botons in this GUI as shown in the following part of the code:
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init_
self.im = None
self.canvas = FigureCanvas(self.figure)
self.canvas.mpl_connect('button_press_event', self.on_button_press_event)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.plot)
self.timer.setInterval(0)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.timer.start)
self.button.setDefault(False)
self.stop = QPushButton("Stop")
self.stop.clicked.connect(self.timer.stop)
self.stop.setDefault(False)
self.exit = QPushButton('Exit')
self.exit.clicked.connect(self.close)
self.exit.setDefault(True)
One has to set the time delay in self.timer.setInterval(0) to Zero! That is the first point.
The part def plot(self) should be changed as follows:
def plot(self):
data=x
self.setWindowTitle("Bestandseingruppierung")
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
#fig.autofmt_xdate()
# discards the old graph
ax.hold(False) # deprecated, see above
# plot data
ax.axes.bar(data,y)
self.canvas.draw()
I write a code to put an imshow in a pyqt environment but the imshow is not centered in the figure:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import numpy as np
import sys
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
class GenerateCM(QMainWindow):
def __init__(self, parent=None):
super(GenerateCM, self).__init__()
self.CM = np.zeros((10,10))
#######################################
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.mainHBOX_param_scene = QHBoxLayout()
#CM view
layout_plot = QHBoxLayout()
self.loaded_plot = CMViewer(self)
self.loaded_plot.setMinimumHeight(200)
self.loaded_plot.update()
layout_plot.addWidget(self.loaded_plot)
self.mainHBOX_param_scene.addLayout(layout_plot)
self.centralWidget.setLayout(self.mainHBOX_param_scene)
class CMViewer(QGraphicsView):
def __init__(self, parent=None):
super(CMViewer, self).__init__(parent)
self.parent=parent
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
# self.canvas.setGeometry(0, 0, 1600, 500 )
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
self.canvas.show()
def update(self):
self.connectivityMat = self.parent.CM
self.figure.clear()
self.axes=self.figure.add_subplot(1,1,1)
im = self.axes.imshow(self.connectivityMat)
self.figure.colorbar(im)
self.axes.axis('off')
self.axes.set_title('Loaded Matrix')
self.canvas.draw()
self.canvas.show()
def main():
app = QApplication(sys.argv)
ex = GenerateCM(app )
ex.show()
sys.exit(app.exec_( ))
if __name__ == '__main__':
main()
Which give me this result.
I would like the image to be in the center of the window and not shifted to the rigth as it is for now. I thing I did something wrong with adding a subplot but I cannot find the right way to correctly put the subplot right in the center
Here is some code I've written (and/or adapted from other sources):
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QMainWindow,
QApplication, QWidget, QVBoxLayout,
QDesktopWidget)
from PyQt5.QtGui import QBrush
from PyQt5.QtCore import Qt
import sys
class View(QGraphicsView):
def __init__(self):
super(View, self).__init__()
self.initScene()
def initScene(self):
self.scene = QGraphicsScene()
self.canvas = Fig()
self.setBackgroundBrush(QBrush(Qt.red))
self.canvas.draw()
self.setScene(self.scene)
self.scene.addWidget(self.canvas)
class Fig(FigureCanvas):
def __init__(self, *args,**kwargs):
self.factor = kwargs.pop("factor", 2)
FigureCanvas.__init__(self, Figure(), *args,**kwargs)
self.plot()
def plot(self):
self.ax = self.figure.add_subplot(111)
data = np.random.rand(1000)
self.ax.plot(data, '-')
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
desktop = QDesktopWidget()
rect = desktop.availableGeometry()
self.setGeometry(rect.width()/10, rect.height()/10, rect.width()/1.2,
rect.height()/1.2)
self.view = View()
self.setCentralWidget(self.view)
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec_()
It produces the following window as its output:
I would like the plot in the middle to occupy as much space as possible, so that the red background becomes invisible.
This happens if I exclude the command setting the size of the window, that is indeed what happens. However, the window is then too small - I need to make it bigger.
I have tried using self.view.setGeometry, but it doesn't seem to make a difference. I've had a look at the available modules in the documentation, but can't tell which might help.
If you want to establish that the plot is shown covering all the available space within the window I see it unnecessary to use QGraphicsView since Fig is a QWidget that can be used directly in setCentralWidget():
class Fig(FigureCanvas):
def __init__(self, *args,**kwargs):
self.factor = kwargs.pop("factor", 2)
FigureCanvas.__init__(self, Figure(), *args,**kwargs)
self.plot()
def plot(self):
self.ax = self.figure.add_subplot(111)
data = np.random.rand(1000)
self.ax.plot(data, '-')
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
desktop = QDesktopWidget()
rect = desktop.availableGeometry()
self.setGeometry(rect.width()/10, rect.height()/10, rect.width()/1.2,
rect.height()/1.2)
self.canvas = Fig()
self.canvas.draw()
self.setCentralWidget(self.canvas)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
update: Using QScrollArea
class Fig(FigureCanvas):
def __init__(self, *args,**kwargs):
self.factor = kwargs.pop("factor", 2)
FigureCanvas.__init__(self, Figure(), *args,**kwargs)
self.plot()
def plot(self):
self.ax = self.figure.add_subplot(111)
data = np.random.rand(1000)
self.ax.plot(data, '-')
def showEvent(self, event):
self.setFixedSize(self.size())
FigureCanvas.showEvent(self, event)
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
desktop = QDesktopWidget()
rect = desktop.availableGeometry()
self.setGeometry(rect.width()/10, rect.height()/10, rect.width()/1.2,
rect.height()/1.2)
self.canvas = Fig()
self.canvas.draw()
scrollArea = QScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setWidget(self.canvas)
self.setCentralWidget(scrollArea)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I get some problems about basemap, where when I want to try panning or zooming map, then the map will be redrawed. Anyone who can give me some clues or methods in this case? Here is some code I included. thanks
(I'm sorry about my bad english)
main.py
from toolbar import tool_bar
from mapping import CanvasFrame
class MainWindow(object):
def __init__(self):
....
self.create_basemap()
def create_basemap(self):
self.canvas = CanvasFrame()
....
self.toolbar = tool_bar(self.canvas, self.window)
self.vbox1 = gtk.VBox(False, 2)
self.button1 = gtk.ToggleButton("zoom")
self.button1.connect("toggled", self.get_zoom)
self.button2 = gtk.ToggleButton("pan")
self.button2.connect("toggled", self.get_pan)
self.button3 = gtk.Button("restore")
self.button3.connect("clicked", self.get_restore)
self.vbox1.pack_start(self.button1)
self.vbox1.pack_start(self.button2)
self.vbox1.pack_start(self.button3)
....
def get_zoom(self, button):
if self.button1.get_active():
self.button2.set_active(False)
self.toolbar._zoom()
def get_pan(self, button):
if self.button2.get_active():
self.button1.set_active(False)
self.toolbar._pan()
def get_restore(self, button):
self.toolbar._restore()
....
mapping.py
from matplotlib.figure import Figure
from mpl_toolkits.basemap import Basemap
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
....
class CanvasFrame(FigureCanvas):
def __init__(self):
self.figure = Figure(dpi=80)
FigureCanvas.__init__(self, self.figure)
self.ax = self.figure.add_subplot(111)
self.plot_map()
def plot_map(self):
self.map = Basemap(projection = 'mill', resolution = 'i', llcrnrlon = 103.5,
llcrnrlat= -6.25,urcrnrlon = 107, urcrnrlat = -3.7, ax = self.ax)
....
toolbar.py
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg
class tool_bar(NavigationToolbar2GTKAgg):
def __init__(self, canvas, window):
super(NavigationToolbar2GTKAgg, self).__init__(canvas, window)
def _zoom(self):
self.zoom()
def _pan(self):
self.pan()
def _restore(self):
self.home()