matplotlib (imshow) 中的每个块应该如何被点击?

Posted

技术标签:

【中文标题】matplotlib (imshow) 中的每个块应该如何被点击?【英文标题】:How every block in matplotlib (imshow) should be made clickable? 【发布时间】:2017-06-16 16:28:41 【问题描述】:

我有 2 个矩阵,我正在使用 matplotlib 通过imshow 显示它们。我使用 PyQt5 将 matplotlib 嵌入到一个小 GUI 中。每个块分别代表矩阵中的每个值。我希望这些块是可点击的。使其可点击的原因是每当点击每个块时,都会弹出另一个 matplotlib 图。在进入这个阶段之前,我希望这些块是可点击的。我怎样才能做到这一点?我已经尝试了几种来自互联网的方法,但没有运气。

界面图片

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 numpy as np
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


j=0
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.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.loop)
        self.button.setDefault(False)

        self.stop = QPushButton("Stop")
        self.stop.clicked.connect(self.loopStop)
        self.stop.setDefault(False)

        self.exit = QPushButton('Exit')
        self.exit.clicked.connect(self.closeIt)
        self.exit.setDefault(True)

        self.leBtn = QPushButton('Enter Vmin')
        self.leBtn.clicked.connect(self.on_pushButtonOK_clicked)
        self.leBtn.setDefault(False)

        self.lineedit = QLineEdit(self)
        validator = QtGui.QIntValidator()
        self.lineedit.setValidator(validator)
        self.lineedit.returnPressed.connect(self.leBtn.click)

        self.leBtn1 = QPushButton('Enter Vmax')
        self.leBtn1.clicked.connect(self.on_pushButtonOK_clicked1)
        self.leBtn1.setDefault(False)

        self.lineedit1 = QLineEdit(self)
        validator = QtGui.QIntValidator()
        self.lineedit1.setValidator(validator)
        self.lineedit1.returnPressed.connect(self.leBtn1.click)


        # 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)
        layout.addWidget(self.lineedit)
        layout.addWidget(self.leBtn)
        layout.addWidget(self.lineedit1)
        layout.addWidget(self.leBtn1)

        self.setLayout(layout)

    def on_pushButtonOK_clicked(self):
        self.vmin = int(self.lineedit.text())

    def on_pushButtonOK_clicked1(self):
        self.vmax=int(self.lineedit1.text())


    def plot(self):
        global j
        if j == 0:
            j += 1
            rows, cols = H.shape

            im = plt.imshow(H, interpolation='nearest', cmap='bwr', vmin=self.vmin, vmax=self.vmax, extent=[0, cols, 0, rows])
            v = np.linspace(0, 1023, 15, endpoint=True)
            ax1 = self.figure.add_axes([0.85, 0.093, 0.04, 0.8])
            cax = self.figure.add_subplot(1, 1, 1)
            self.figure.colorbar(im, cax=ax1, orientation='vertical', ticks=v)
            self.canvas.draw()

        elif j == 1:
            j -= 1
            rows, cols = H.shape

            im = plt.imshow(Z, interpolation='nearest', cmap='Spectral', vmin=self.vmin, vmax=self.vmax,
                        extent=[0, cols, 0, rows])
            v = np.linspace(0, 1023, 15, endpoint=True)
            ax1 = self.figure.add_axes([0.85, 0.093, 0.04, 0.8])
            cax = self.figure.add_subplot(1, 1, 1)
            self.figure.colorbar(im, cax=ax1, orientation='vertical', ticks=v)
            self.canvas.draw()

    def loop(self):
        self.timer = QtCore.QTimer()
        self.timer.setSingleShot(False)
        self.timer.timeout.connect(self.plot)
        self.timer.start(1000)

    def loopStop(self):
        self.timer.stop()

    def closeIt(self):
        self.close()



if __name__ == '__main__':
    app = QApplication(sys.argv)

    main = Window()
    main.show()

    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

如果你想捕捉点击事件,你必须通过画布和mpl_connect('button_press_event', some_callback)方法:

class Window(QDialog):
    def __init__(self, parent=None):
        [...]
        self.canvas = FigureCanvas(self.figure)
        self.canvas.mpl_connect('button_press_event', self.on_button_press_event)
        [...]

    def on_button_press_event(self, event):
        print(event)

示例:

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 numpy as np
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


j=0
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)

        self.leBtn = QPushButton('Enter Vmin')
        self.leBtn.clicked.connect(self.on_pushButtonOK_clicked)
        self.leBtn.setDefault(False)

        self.lineedit = QLineEdit(self)
        validator = QtGui.QIntValidator()
        self.lineedit.setValidator(validator)
        self.lineedit.returnPressed.connect(self.leBtn.click)

        self.leBtn1 = QPushButton('Enter Vmax')
        self.leBtn1.clicked.connect(self.on_pushButtonOK_clicked1)
        self.leBtn1.setDefault(False)

        self.lineedit1 = QLineEdit(self)
        validator = QtGui.QIntValidator()
        self.lineedit1.setValidator(validator)
        self.lineedit1.returnPressed.connect(self.leBtn1.click)


        # 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)
        layout.addWidget(self.lineedit)
        layout.addWidget(self.leBtn)
        layout.addWidget(self.lineedit1)
        layout.addWidget(self.leBtn1)

        self.setLayout(layout)

        self.lb = QtWidgets.QLabel(self)
        self.lb.setWindowFlags(QtCore.Qt.ToolTip)

    def on_pushButtonOK_clicked(self):
        self.vmin = int(self.lineedit.text())

    def on_pushButtonOK_clicked1(self):
        self.vmax=int(self.lineedit1.text())


    def plot(self):
        global j
        if j == 0:
            j += 1
            rows, cols = H.shape

            self.im = plt.imshow(H, interpolation='nearest', cmap='bwr', vmin=self.vmin, vmax=self.vmax, extent=[0, cols, 0, rows])
            v = np.linspace(0, 1023, 15, endpoint=True)
            ax1 = self.figure.add_axes([0.85, 0.093, 0.04, 0.8])
            cax = self.figure.add_subplot(1, 1, 1)
            self.figure.colorbar(self.im, cax=ax1, orientation='vertical', ticks=v)
            self.canvas.draw()

        elif j == 1:
            j -= 1
            rows, cols = H.shape

            self.im = plt.imshow(Z, interpolation='nearest', cmap='Spectral', vmin=self.vmin, vmax=self.vmax,
                        extent=[0, cols, 0, rows])
            v = np.linspace(0, 1023, 15, endpoint=True)
            ax1 = self.figure.add_axes([0.85, 0.093, 0.04, 0.8])
            cax = self.figure.add_subplot(1, 1, 1)
            self.figure.colorbar(self.im, cax=ax1, orientation='vertical', ticks=v)
            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_())

注意:我将 im 变量更改为 self.im 以便能够访问显示在 imshow 中的值

【讨论】:

非常感谢您帮助我。至少我知道这是如何工作的。现在我想要的是,当单击特定块时,应该会弹出一个 matplotlib 窗口,其中包含一些随机数据。我只需要一点想法。再次感谢 如果我的回答能帮助您将我的回答标记为正确,请 我更新了示例,显示了一个包含单元格内容的弹出窗口 我真的很感激。再次感谢。标记为正确。 @HammadUllah 由于您使用正方形,您可以使用 xclick 和 yclick 并确定使用舍入的正方形。像正方形 (1,1) 是 roundup(xclick)==1 and roundup(yclick)==1

以上是关于matplotlib (imshow) 中的每个块应该如何被点击?的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib中的imshow()

Matplotlib imshow 和 kivy

matplotlib imshow扭曲颜色[重复]

在 matplotlib imshow 中调整网格线和刻度线

同一个 imshow matplotlib 中的两个不同颜色的颜色图

如何以交互方式更新 matplotlib imshow() 窗口?