Issue
I am building an application in PyQt5. I want to use seaborn to visualize a matrix and update the heatmap created by seaborn when the matrix data is changed. I create the original plot like this:
from matplotlib.figure import Figure
import numpy as np
import seaborn as sns
class MplWidget_pcolormesh(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
self.clb = []
self.plot = []
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
self.canvas.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.canvas.toolbar)
self.layout().addWidget(self.canvas)
X = np.array([[0, 1], [1, 0]])
self.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.axes, vmin=0, vmax=1)
This only creates a simple heatmap plot that is later updated:
Simple heatmap to begin with:
Then, in another py file I want to update it like this:
def matrixPlot(self, selected):
X = ... # generating new data
self.view.MplWidget.axes.clear()
self.view.MplWidget.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.view.MplWidget.axes, vmin=0, vmax=1)
self.view.MplWidget.canvas.draw()
It does update the plot, but it always regenerates the colorbar:
Now I tried to save the axis objects, so I can just update them, but when I try creating the subplot like this:
fig, (ax, cbarax) = self.axes = self.canvas.figure.add_subplot(111)
I get the error:
TypeError: cannot unpack non-iterable AxesSubplot object
How could I create the heatmap in a way, that I can update the content and the colorbar values later, without creating a number of colorbars?
EDIT (@eyllanesc):
You can get the same result by building this project:
simpleMatrixGUI.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'simpleMatrixGUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.MplWidget = MplWidget(self.centralwidget)
self.MplWidget.setObjectName("MplWidget")
self.verticalLayout.addWidget(self.MplWidget)
self.randMatrixGenerator = QtWidgets.QPushButton(self.centralwidget)
self.randMatrixGenerator.setObjectName("randMatrixGenerator")
self.verticalLayout.addWidget(self.randMatrixGenerator)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.randMatrixGenerator.setText(_translate("MainWindow", "Generate new"))
from mplwidget import MplWidget
Windows.py
from PyQt5.QtWidgets import QMainWindow
from simpleMatrixGUI import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
Main.py
import sys
from PyQt5.QtWidgets import QApplication
from MainPresenter import MainPresenter
from Windows import MainWindow
if __name__ == '__main__':
# Create the application (with optional system arguments)
app = QApplication(sys.argv)
# Create model, view and presenter objects
model = None
view = MainWindow()
presenter = MainPresenter(view=view, model=model)
# Start the main app event loop
exit_code = app.exec_()
# Perform system exit to safely quit (relay the app exit code)
sys.exit(exit_code)
mplwidget.py
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import numpy as np
import seaborn as sns
class MplWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
self.clb = []
self.plot = []
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
self.canvas.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.canvas.toolbar)
self.layout().addWidget(self.canvas)
X = np.random.randn(10, 8)
self.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.axes, vmin=np.min(X), vmax=np.max(X))
MainPresenter.py
from typing import Optional
import numpy as np
from Windows import MainWindow
import seaborn as sns
class MainPresenter:
def __init__(self, view: MainWindow, model: Optional[int] = None):
self.view = view
self.model = model
self.view.show()
view.randMatrixGenerator.clicked.connect(self.matrixPlot)
def matrixPlot(self, selected):
X = np.random.randn(10,8)
self.view.MplWidget.axes.clear()
self.view.MplWidget.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.view.MplWidget.axes, vmin=np.min(X), vmax=np.max(X))
self.view.MplWidget.canvas.draw()
Solution
You could create the colorbar axis in this way:
grid_kws = {'width_ratios': (0.9, 0.05), 'wspace': 0.2}
fig, (ax, cbar_ax) = plt.subplots(1, 2, gridspec_kw = grid_kws, figsize = (10, 8))
Then, you can pass cbar_ax
to sns.heatmap
:
sns.heatmap(ax = ax, cbar_ax = cbar_ax, ...)
Have a look to this answer for a reference.
EDIT
If you apply the concepts above to your files, you need to edit them in this way:
mplwidget.py
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
class MplWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
grid_kws = {'width_ratios': (0.9, 0.05), 'wspace': 0.2}
self.figure, (self.axes, self.cbar_ax) = plt.subplots(1, 2, gridspec_kw = grid_kws, figsize = (10, 8))
self.canvas = FigureCanvas(self.figure)
self.clb = []
self.plot = []
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.setLayout(vertical_layout)
self.canvas.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.canvas.toolbar)
self.layout().addWidget(self.canvas)
X = np.random.randn(10, 8)
self.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.axes, vmin=np.min(X), vmax=np.max(X), cbar_ax = self.cbar_ax)
MainPresenter.py
from typing import Optional
import numpy as np
from Windows import MainWindow
import seaborn as sns
class MainPresenter:
def __init__(self, view: MainWindow, model: Optional[int] = None):
self.view = view
self.model = model
self.view.show()
view.randMatrixGenerator.clicked.connect(self.matrixPlot)
def matrixPlot(self, selected):
X = np.random.randn(10,8)
self.view.MplWidget.axes.clear()
self.view.MplWidget.plot = sns.heatmap(X, cmap='PuBu', square=True, linewidth=0.1, linecolor=(0.1, 0.2, 0.2),
ax=self.view.MplWidget.axes, vmin=np.min(X), vmax=np.max(X), cbar_ax = self.view.MplWidget.cbar_ax)
self.view.MplWidget.canvas.draw()
Answered By - Zephyr
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.