Embedding second animated graph to PyQt5 GUI - python

I want to add a second animated graph to the GUI holding my first animated graph with both graphs animating at the same time, but I'm not sure how.
Here is my code :
import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
from matplotlib import animation
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.fig = Figure(figsize=(5, 3))
self.canvas = FigureCanvas(self.fig)
layout.addWidget(self.canvas)
self.addToolBar(NavigationToolbar(self.canvas, self))
self.setup()
def setup(self):
self.ax = self.fig.subplots()
self.ax.set_aspect('equal')
self.ax.grid(True, linestyle = '-', color = '0.10')
self.ax.set_xlim([-15, 15])
self.ax.set_ylim([-15, 15])
self.scat = self.ax.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat.set_alpha(0.8)
self.anim = animation.FuncAnimation(self.fig, self.update,
frames = 720, interval = 10)
def update(self, i):
self.scat.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
And here is some sample code which has two graphs inside the same window (like how I want to)
Here they use an _update_canvas function for the animated graph and the other graph (which is just a static graph) they plot it in the application window class.
I'm using an update plot function to animate my graph, do I need a second update plot function? How?
Sample code:
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(static_canvas)
self.addToolBar(NavigationToolbar(static_canvas, self))
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(dynamic_canvas, self))
self._static_ax = static_canvas.figure.subplots()
t = np.linspace(0, 10, 501)
self._static_ax.plot(t, np.tan(t), ".")
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
self._dynamic_ax.clear()
t = np.linspace(0, 10, 101)
# Shift the sinusoid as a function of time.
self._dynamic_ax.plot(t, np.sin(t + time.time()))
self._dynamic_ax.figure.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

To embed multiple animated graphs, you need to create multiple plot objects(Figure and FigureCanvas) then add each object to the QVBoxLayout. If you wanted to display it horizontally, you can use a QHBoxLayout. Each plot object will have its own subplot, grid, and data. To update each plot's data, you will need each plot to have its individual plot update function where you can pass this to the animation.FuncAnimation handler. So in your case, to have two animated graphs, you will need two update plot functions.
from matplotlib.figure import Figure
from matplotlib import animation
import numpy as np
import sys, matplotlib
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
# Configure figure 1
self.fig1 = Figure(figsize=(5, 3))
self.canvas1 = FigureCanvas(self.fig1)
# Configure figure 2
self.fig2 = Figure(figsize=(5, 3))
self.canvas2 = FigureCanvas(self.fig2)
layout.addWidget(self.canvas1)
layout.addWidget(self.canvas2)
self.addToolBar(NavigationToolbar(self.canvas1, self))
self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.canvas2, self))
self.setup()
def setup(self):
# Plot 1 (top)
self.ax1 = self.fig1.subplots()
self.ax1.set_aspect('equal')
self.ax1.grid(True, linestyle = '-', color = '0.10')
self.ax1.set_xlim([-15, 15])
self.ax1.set_ylim([-15, 15])
# Plot 2 (bottom)
self.ax2 = self.fig2.subplots()
self.ax2.set_aspect('equal')
self.ax2.grid(True, linestyle = '-', color = '0.10')
self.ax2.set_xlim([-15, 15])
self.ax2.set_ylim([-15, 15])
self.scat1 = self.ax1.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat1.set_alpha(0.8)
self.scat2 = self.ax2.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat2.set_alpha(0.8)
self.anim1 = animation.FuncAnimation(self.fig1, self.update1,frames = 720, interval = 10)
self.anim2 = animation.FuncAnimation(self.fig2, self.update2,frames = 720, interval = 10)
# Update data for plot 1
def update1(self, i):
self.scat1.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
# Update data for plot 2
def update2(self, i):
self.scat2.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

Is there a specific reason you need two separate canvas and two separate figures? If not, then I agree with ImportanceOfBeingErnest's comment and that you should create only one figure/canvas with 2 subplots, and call a single update function that takes care of updating the content of both axes.
In essence, your question would be a duplicate of this one, except for the fact that you are embedding the animation in a Qt app.
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.fig = Figure(figsize=(5, 6))
self.canvas = FigureCanvas(self.fig)
layout.addWidget(self.canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(self.canvas, self))
self._axs = self.fig.subplots(2, 1)
self._timer = self.canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
[ax.clear() for ax in self._axs]
t = np.linspace(0, 10, 501)
self._axs[0].plot(t, np.tan(t + time.time()), ".")
t = np.linspace(0, 10, 101)
self._axs[1].plot(t, np.sin(t + time.time()))
self.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

Related

Plotting a chart inside a PyQT GUI

I would like to plot a chart inside a pyQT GUI. I have already read a lot of tutorials, but those plot the chart in a separeted window. I need it to be ploted in the same window as the other buttons and fields.
In my main.py file, I import the Ui_MainWindow class generated with the QT designer. But I didn't figured out how to plot it inside the MainWindow
Python GUI
Matplotlib canvas class to create figure
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def atualizar(self):
global tempoInicio
tempoInicio = self.campoTempoInicio.dateTime()
print(tempoInicio.toPyDateTime())
x=range(0, 10)
y=range(0, 20, 2)
self.FigureCanvas.canvas.ax.plot(x, y)
self.FigureCanvas.canvas.draw()
I've tried as this, but it didn't worked as well (followed a tutorial)
Thank you in advance for reading this.
After looking into non-relatated tutorial, I've foud this one: https://www.pythonguis.com/tutorials/embed-pyqtgraph-custom-widgets-qt-app/
that teaches how to do what I need.
Don't now why it didn't appear before. But worked!
Here's a complete example that should help:
import sys
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
class PlotViewer(QtWidgets.QWidget):
doubleClickAction = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(PlotViewer, self).__init__(parent)
self.figure = plt.figure(figsize=(5, 5))
self.figureCanvas = FigureCanvas(self.figure)
self.navigationToolbar = NavigationToolbar(self.figureCanvas, self)
# create main layout
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.navigationToolbar)
layout.addWidget(self.figureCanvas)
self.setLayout(layout)
# create an axis
x = range(0, 10)
y = range(0, 20, 2)
ax = self.figure.add_subplot(111)
ax.plot(x, y)
# show canvas
self.figureCanvas.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
widget = PlotViewer()
widget.show()
app.exec_()

How to persist changes made by the NavigationToolbar on a periodically updated graph

I have a PyQt5 GUI application. This application shows a graph (with my default xlim and ylim) that gets updated every second, a real-time graph basically. This functionality I have, but I want to add a NavigationToolbar so one can zoom in/out the graph.
I added the toolbar to my layout and it gets displayed. So far so good. Now I zoom in, the graph gets zoomed in, but once the graph gets periodically updated the xlim and ylim are defaulted again and the zoom is gone. What properties I need to call from the toolbar so I can save them and pass them to my _update_canvas function? I looked at https://matplotlib.org/3.1.0/api/axes_api.html, and noticed the function get_ylim. So i tried as followed:
self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim()) and self._dynamic_ax.set_ylim(self._dynamic_ax2.get_ylim())
As well as:
self._dynamic_ax.set_navigate(True)
However these didn't work. How can I persist the settings set by NavigationToolbar? Not only the zoom but also the pan.
A minimal runnable code sample:
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
buffer_size = 120
t = [t for t in range(buffer_size)]
bitthrough = [t for t in range(buffer_size)]
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
self._dynamic_ax.clear()
self._dynamic_ax.plot(t, bitthrough, color="black")
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120) #self._dynamic_ax.get_ylim())
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
self._dynamic_ax2.clear()
self._dynamic_ax2.plot(t, errors, color="blue")
self._dynamic_ax2.set_ylabel("Errors", color="blue")
self._dynamic_ax2.set_ylim(-4, 100) #self._dynamic_ax2.get_ylim())
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
My real _update_canvas function:
def _update_canvas(self):
wh_green = [a <= b for a, b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c
for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
# self._dynamic_ax.clear()
# self._dynamic_ax2.clear()
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_red, color="red", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_orange, color="orange", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_green, color="green", interpolate=True
)
# self._dynamic_ax.plot(t, bitthrough, color="black")
# self._dynamic_ax.set_xlabel("time (s)")
# self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
# #self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim())
# self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax2.plot(t, errors, color="blue")
# self._dynamic_ax2.set_ylabel("Errors", color="blue")
# #self._dynamic_ax2.set_ylim(self._dynamic_ax2.get_ylim())
# self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
# logging.debug("redrawing graph!!")
self._dynamic_ax.figure.canvas.draw_idle()
The solution of #DizietAsahi doesn't work while using fill_between. The area gets overwritten and not cleared. So they are displayed on top of eachother.
My advice would be to not clear the figure at each update. Instead, store a reference to the Line2D artists created by plot() and update the {x|y}data (using set_data() or set_ydata()) in your update function.
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
buffer_size = 120
t = np.linspace(0, 100, buffer_size)
bitthrough = 120*np.random.random(size=(buffer_size,))
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
##
## Create plots here (initially empty)
##
self._plot1, = self._dynamic_ax.plot(t, np.empty(shape=(buffer_size,)), color="black")
self._plot2, = self._dynamic_ax2.plot(t, np.empty(shape=(buffer_size,)), color="blue")
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
bitthrough = 120*np.random.random(size=(buffer_size, ))
errors = bitthrough[::-1]
##
## update the content of the plots here, without clearing the figure
##
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
self._fill1.remove()
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
EDIT
I've added some code for handling fill_between().
fill_between() returns a PolyCollection which is a pain to update, so the best option there is to remove the PolyCollection and re-create it at each update (but not clear the whole figure).

Add/Delete plots independently on a Matplotlib figure

I want to generate a scatterplot (up to half a million points) and on top of that, add different statistics (e.g. Q1, median, Q3). The idea is to add/delete those statistics without replotting the scatterplot in order to speed up the process. So far I can add plots independently on the figure but I can't delete a specific plot.
When I uncheck the checkbox, I get the following error:
AttributeError: 'Graphics' object has no attribute 'vline1'
I understand that when I create the plot, I need to store/return the plot in order to call it later when I want to delete it but I don't know how to do that.
Here my current code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.pyplot import Figure
class Mainwindow(QMainWindow):
def __init__(self, parent=None):
super(Mainwindow, self).__init__(parent)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.gridLayout = QGridLayout(centralWidget)
self.gridLayout.addWidget(self.canvas)
self.btn_plot = QCheckBox("Plot")
self.btn_line = QCheckBox("Line")
self.gridLayout.addWidget(self.btn_plot, 1,0,1,1)
self.gridLayout.addWidget(self.btn_line, 2,0,1,1)
self.btn_plot.clicked.connect(self.btnPlot)
self.btn_line.clicked.connect(self.btnLine)
def btnPlot(self):
self.checked = self.btn_plot.isChecked()
self.Graphics = Graphics('plot', self.checked, self.axes)
def btnLine(self):
self.checked = self.btn_line.isChecked()
self.Graphics = Graphics('line', self.checked, self.axes)
class Graphics:
def __init__(self, typeGraph, checked, axes):
self.typeGraph = typeGraph
self.checked = checked
self.axes = axes
if self.typeGraph == 'plot': self.drawPlot()
if self.typeGraph == 'line': self.drawLine()
def drawPlot(self):
if self.checked == True:
self.plot = self.axes.plot([10,20,30], [5,10,2], 'o')
else:
self.plot.remove()
self.axes.figure.canvas.draw()
def drawLine(self):
if self.checked == True:
self.vline1 = self.axes.axvline(x=15, linestyle="dashed", color="#595959")
self.vline2 = self.axes.axvline(x=25, linestyle="dashed", color="#595959")
else:
self.vline1.remove()
self.vline2.remove()
self.axes.figure.canvas.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
prog = Mainwindow()
prog.show()
sys.exit(app.exec_())
Problem is be because when you click it then it creates always new Graphics (in btnPlot/btnLine) which doesn't have previous values - plot, vline1, vline2. You have to create Graphics only once and later run only drawPlot(checked), drawLine(checked) to add or remove item.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.pyplot import Figure
class Mainwindow(QMainWindow):
def __init__(self, parent=None):
super(Mainwindow, self).__init__(parent)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.gridLayout = QGridLayout(centralWidget)
self.gridLayout.addWidget(self.canvas)
self.btn_plot = QCheckBox("Plot")
self.btn_line = QCheckBox("Line")
self.gridLayout.addWidget(self.btn_plot, 1,0,1,1)
self.gridLayout.addWidget(self.btn_line, 2,0,1,1)
self.btn_plot.clicked.connect(self.btnPlot)
self.btn_line.clicked.connect(self.btnLine)
# create only once
self.Graphics = Graphics(self.axes)
def btnPlot(self):
# add or remove
self.Graphics.drawPlot(self.btn_plot.isChecked())
def btnLine(self):
# add or remove
self.Graphics.drawLine(self.btn_line.isChecked())
class Graphics:
def __init__(self, axes):
self.axes = axes
# create at start with default values (but frankly, now I don't need it)
self.plot = None
self.vline1 = None
self.vline2 = None
def drawPlot(self, checked):
if checked:
self.plot = self.axes.plot([10,20,30], [5,10,2], 'o')
else:
for item in self.plot:
item.remove()
self.axes.figure.canvas.draw()
def drawLine(self, checked):
if checked:
self.vline1 = self.axes.axvline(x=15, linestyle="dashed", color="#595959")
self.vline2 = self.axes.axvline(x=25, linestyle="dashed", color="#595959")
else:
self.vline1.remove()
self.vline2.remove()
self.axes.figure.canvas.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
prog = Mainwindow()
prog.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

Why isn't my line graph showing up on GUI

This is a function which is being called on a button click. It is supposed to display a line graph why is it not showing up.
def plotk2(self):
#----------------------------------------------------------------Data
temp=full_dataset[['country_txt','iyear','nkill']]
text = self.k_count.currentText()
temp = temp[temp['country_txt'].str.match(text)]
temp2=temp.groupby(['iyear'])['nkill'].count()
temp2=temp2.to_frame()
temp2['iyear']=temp2.index
#----------------------------------------------------------------
self.figure.clear()
ax = self.figure.add_subplot(111)
plt.plot( 'nkill', 'iyear', data=temp2)
#ax.axis('off')
plt.show()
self.canvas.draw()
If you want to draw on PyQt you must use FigureCanvas, not matplotlib.pyplot, so if you use plt.plot() you will not draw anything.
In your case a similar example would be:
import sys
import pandas as pd
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.figure = Figure(figsize=(5, 4), dpi=100)
self.canvas = FigureCanvas(self.figure)
self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
button = QtWidgets.QPushButton("random plot")
button.clicked.connect(self.plot)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.canvas)
lay.addWidget(button)
self.plot()
def plot(self):
self.figure.clear()
ax = self.figure.add_subplot(111)
d = {'nkill': [1, 2, 4, 5, 6], 'iyear': [3, 4, 5, 5, 5]}
df = pd.DataFrame(data=d)
ax.plot('nkill', 'iyear', data=df)
# ax.axis('off')
self.canvas.draw()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Categories

Resources