Using PyQt5 to embed a dynamical bar chart - python

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()

Related

How to make matplotlib widget in PyQt5 clickable?

I am working on GUI where I have tab system with graphs. I want that if a user clicks (or puts cursor) at any point in the graph, it shows the exact x and y values in that point like that:
I know that in usual matplotlib it is easy to implement; however I do not know how to do that in PyQt5.
My tabs system and canvas look like that:
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QApplication, QWidget,QVBoxLayout,QTabWidget
import sys
import matplotlib.pyplot as plt
def mouse_move(event):
x, y = event.xdata, event.ydata
print(x, y)
plt.connect('motion_notify_event', mouse_move)
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi=80):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.plot()
def plot(self):
x = ['22-02 11:16:15', '22-02 15:31:54', '22-02 15:32:30',
'22-02 15:32:45', '22-02 15:33:57', '22-02 15:34:13',
'22-02 15:34:46']
y = [1, 4, 3, 4, 8, 9, 2]
self.figure.tight_layout()
ax = self.figure.add_subplot(111)
ax.plot(x, y)
class MainWindow(QDialog):
def __init__(self):
super().__init__()
self.top = 255
self.left = 150
self.setGeometry(self.left, self.top, 900, 900)
self.Mainlayout = QVBoxLayout(self)
self.tabs = QTabWidget()
self.graphUP = QWidget()
self.graphUP.layout = QVBoxLayout( self.graphUP)
self.graphUP.layout.addWidget(Canvas())
self.tabs.setFixedHeight(800)
self.tabs.setFixedWidth(800)
self.tabs.addTab(self.graphUP, "Graph1")
self.Mainlayout.addWidget(self.tabs)
self.show()
if __name__ == '__main__':
App = QApplication(sys.argv)
window = MainWindow()
sys.exit(App.exec())
import this module:
import mplcursors as mpl
and add : mpl.cursor(hover=True)
in your def plot() function.

relim of matplotlib axes (embedded in pyqt)doesn't work is I previously zoomed in

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()

How to send variables to a def through an event and return the variable

How do you send variables through events? I want to be able to return the x,y coordinates of a scatterplot datapoint that was clicked. Currently the program is able to perform the click event but I cant pull out the x, y data to use later on in the program. I added a Coordinate button as an example of using the data afterwards. Thanks in advance.
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
def onclick(event):
ind = event.ind[0]
data = event.artist.get_offsets()
xdata, ydata = data[ind,:]
print ((xdata, ydata))
return (xdata,ydata)# i want to return these
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.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
self.button2 = QPushButton('Coordinate')
self.button2.clicked.connect(self.coordinate)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
layout.addWidget(self.button2)
self.setLayout(layout)
self.canvas.mpl_connect('pick_event', onclick)
def coordinate(self):
print(xdata, ydata)#these variables are not being returned
def plot(self):
x = [10,20,30,40,50]
y = [100,50,150,200,75]
size = 100
# instead of ax.hold(False)
self.figure.clear()
# create an axis
self.axes = self.figure.add_subplot(111)
# discards the old graph
self.axes.scatter(x,y,s=size,color='blue', picker=1)
self.axes.patch.set_facecolor('None')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())

Rotate or resize the axis ticks using pyqt5 in python

I use the following code
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
from matplotlib.figure import Figure
# data
import numpy as np
#import matplotlib.pyplot as plt
import pandas as pd
import os
import csv
# percent MW
x=['BE2000',
'BE2020',
'BE5000',
'BE6010',
'BE6017',
'BE6020',
'BE6027',
'BE6030',
'BE6050',
'BE6057',
'BE6061',
'BE6062',
'BE6063',
'BE6070',
'BE6073',
'BE6075',
'BE6080',
'BE6090',
'BE7000',
'BE7010']
y=[0.0002716766988612973,
0.005178087393490427,
0.0014053668695097226,
0.3174139251746979,
0.006049724003653125,
0.24824287385322272,
0.0004986331396716525,
0.19624266416568525,
0.13170894569069627,
0.0028535946936992873,
0.0002737864422892905,
0.0011817396106664916,
0.0029533382584451574,
0.03281361420815501,
0.000273411124091493,
0.002558801432193384,
0.027004861403488886,
0.004918720459633545,
0.006030278629435105,
0.0002858345295419518]
#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(500)
# 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)
# set the layout
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):
data=x
self.setWindowTitle("Bestandseingruppierung")
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
plt.setp(ax.get_xticklabels(), rotation=45)
#fig.autofmt_xdate()
# discards the old graph
ax.hold(False) # deprecated, see above
# plot data
ax.axes.bar(data,y)
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_())
to generate a dynamical bar chart. Now there is a problem in the the plot.
.
I tried ax.set_xticklabels to rotate the ticks on the x-axis in two different positions:
1) directly after creating the figure:
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
self.figure.set_xticklabels(rotation=45)
which produces an error.
2) as the second attempt I placed it in
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)
ax.set_xticklabels(rotation=45)
#fig.autofmt_xdate()
# discards the old graph
ax.hold(False) # deprecated, see above
# plot data
ax.axes.bar(data,y)
self.canvas.draw()
In this case I get the old plot without any error but any rotation!
I would like to know which options and where should I place to get either an rotation of the ticks or to resize the ticks or labels by using pyqt5!
Your second attempt was quite close to right way!
Just add your x-labels to ax.set_xticklabels(rotation=45) as a first positional parameter.
So, copy this line of code and place it into your second attempt:
ax.set_xticklabels(labels=xlabels, rotation=45)
where xlabels = list of your x-axis labels

Imshow in pyQt is not centered

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

Categories

Resources