簡體   English   中英

當 matplotlib.FigureCanvas 嵌入到 PyQt5 應用程序中時,Matplotlib 回調不起作用?

[英]Matplotlib callbacks don't work when matplotlib.FigureCanvas is embedded in a PyQt5 application?

Prithee,StackOverflow 的干癟長老,

我正在嘗試使用 PyQt5 制作一個小型 GUI 應用程序,將 matplotlib.FigureCanvas 對象用作小部件來顯示數據,我還想通過使用 matplotlib 的回調函數使數據更具交互性。 但是,這些回調似乎不適用於嵌入在 PyQt5 窗口中的 FigureCanvas。

我認為實例化 Qt5 應用程序可能會干擾 matplotlib 的事件處理程序,但我不確定如何繼續。 有沒有辦法讓 matplotlib 事件引發 Qt5 信號? 我可以有兩個單獨的線程,兩個事件處理程序都可以在其中運行嗎?

下面的示例是我可以縮減的最簡單的事情,它說明了這個問題。

案例 A:在 Matplotlib 窗口中運行

首先按原樣運行,注意所需的行為: onclick函數在圖形彈出后在單擊時調用。 (還要注意第二個代碼塊,案例 B 不會在第一個之后運行)

案例 B:在 PyQt5 中運行

現在,注釋掉 Case A 代碼塊,然后運行:點擊圖中沒有調用回調函數。

import sys
import numpy as np

from PyQt5 import QtCore, QtWidgets, uic

import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pylab as plt
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
matplotlib.rcParams["toolbar"] = "toolmanager"

def onclick(event):
    '''example callback function'''
    print('got click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
          ( event.button, event.x, event.y, event.xdata, event.ydata))

class MPLWidget(QtWidgets.QWidget):
    '''
    Widget which should act like a matplotlib FigureCanvas, but embedded in PyQt5
    '''
    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        
        # Making Matplotlib figure object with data
        fig, ax = plt.subplots()
        fig.suptitle("Plot in PyQt5: click and look in console")
        ax.plot(np.linspace(0,1,2))
        # Attaching matplotlib callback function which doesnt seem to work
        cid = fig.canvas.mpl_connect('button_press_event', onclick)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)      
        self.plotWidget = FigureCanvas(fig)
        self.toolbar = NavigationToolbar(self.plotWidget, self)

        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.plotWidget)


class TestWindow(QtWidgets.QMainWindow):
    '''Window which is a stand in for the larger PyQt5 application which needs a plot embedded'''
    def __init__(self):
        super().__init__()
        self.mplwidget = MPLWidget(self)
        self.setCentralWidget(self.mplwidget)

if __name__ == '__main__':

    # Case A: Using matplotlib without PyQt5, 
    # Note that the callback is triggered and something is printed when you click in the figure
    fig, ax = plt.subplots()
    fig.suptitle("Standalone matplotlib: click and look in console")
    ax.plot(np.linspace(0,1,2))
    cid = fig.canvas.mpl_connect('button_press_event', onclick)
    plt.show()
    
    # Case B:  Running PyQt5 + Matplotlib widget
    # Note that nothing is printed when you click in the figure
    app = QtWidgets.QApplication(sys.argv)
    window = TestWindow()
    window.show()
    sys.exit(app.exec_())

如果您要使用 pyqt5 中的 FigureCanvas,則不必使用“plt”,而是使用 Figure() 進行構建,如官方示例所示:

import sys
import numpy as np

from PyQt5 import QtCore, QtWidgets

import matplotlib

matplotlib.use("QT5Agg")

from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar

matplotlib.rcParams["toolbar"] = "toolmanager"

from matplotlib.figure import Figure


def onclick(event):
    """example callback function"""
    print(
        "got click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f"
        % (event.button, event.x, event.y, event.xdata, event.ydata)
    )


class MPLWidget(QtWidgets.QWidget):
    """
    Widget which should act like a matplotlib FigureCanvas, but embedded in PyQt5
    """

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)

        # Making Matplotlib figure object with data
        self.canvas = FigureCanvas(Figure())
        ax = self.canvas.figure.add_subplot(111)
        ax.set_title("Plot in PyQt5: click and look in console")
        ax.plot(np.linspace(0, 1, 2))

        cid = self.canvas.mpl_connect("button_press_event", onclick)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.toolbar = NavigationToolbar(self.canvas, self)

        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.canvas)


class TestWindow(QtWidgets.QMainWindow):
    """Window which is a stand in for the larger PyQt5 application which needs a plot embedded"""

    def __init__(self):
        super().__init__()
        self.mplwidget = MPLWidget(self)
        self.setCentralWidget(self.mplwidget)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = TestWindow()
    window.show()
    sys.exit(app.exec_())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM