关于matplotlib:在PyQt中嵌入”图类型” Seaborn图(pyqtgraph)

Embedding “Figure Type” Seaborn Plot in PyQt (pyqtgraph)

我正在使用PyQt的package(pyqtgraph)来构建GUI应用程序。
我希望使用MatplotlibWidget在其中嵌入一个Seaborn图。但是,我的问题是,诸如FacetGrid之类的Seabornpackage器方法不接受外部图形句柄。而且,当我尝试用FacetGrid生成的图形更新底层图形(.fig)的MatplotlibWidget对象时,它不起作用(draw之后无图形)。有任何解决方法的建议吗?


Seaborn的Facetgrid提供了一项便捷功能,可将pandas数据帧快速连接到matplotlib pyplot接口。

但是,在GUI应用程序中,您很少要使用pyplot,而是要使用matplotlib API。

您在此处面临的问题是Facetgrid已经创建了自己的matplotlib.figure.Figure对象(Facetgrid.fig)。另外,MatplotlibWidget
创建自己的图形,因此最终得到两个图形。

现在,让我们退后一步:
原则上,可以通过在PyQt中使用海洋的Facetgrid绘图,方法是先创建该绘图,然后将生成的图形提供给图形画布(matplotlib.backends.backend_qt4agg.FigureCanvasQTAgg)。以下是如何执行此操作的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import sys
import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")


def seabornplot():
    g = sns.FacetGrid(tips, col="sex", hue="time", palette="Set1",
                                hue_order=["Dinner","Lunch"])
    g.map(plt.scatter,"total_bill","tip", edgecolor="w")
    return g.fig


class MainWindow(QtGui.QMainWindow):
    send_fig = QtCore.pyqtSignal(str)

    def __init__(self):
        super(MainWindow, self).__init__()

        self.main_widget = QtGui.QWidget(self)

        self.fig = seabornplot()
        self.canvas = FigureCanvas(self.fig)

        self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding,
                      QtGui.QSizePolicy.Expanding)
        self.canvas.updateGeometry()
        self.button = QtGui.QPushButton("Button")
        self.label = QtGui.QLabel("A plot:")

        self.layout = QtGui.QGridLayout(self.main_widget)
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.show()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    sys.exit(app.exec_())

虽然这很好用,但是如果真的有用的话,就有点可疑了。在大多数情况下,在GUI内创建图的目的是根据用户的交互来更新信息。在上面的示例中,这是非常低效的,因为它需要创建一个新的图形实例,用该图形创建一个新的画布,并用新的画布实例替换旧的画布实例,然后将其添加到布局中。

请注意,此问题是特定于seaborn中的绘图功能的,这些功能在图形级别起作用,例如lmplotfactorplotjointplotFacetgrid以及其他。
regplotboxplotkdeplot之类的其他函数在轴级别上运行,并接受matplotlib axes对象作为参数(sns.regplot(x, y, ax=ax1))。

一个可行的解决方案是首先创建子图轴,然后再绘制到那些轴,例如使用pandas绘制功能。

1
df.plot(kind="scatter", x=..., y=..., ax=...)

其中ax应该设置为先前创建的轴。
这允许在GUI中更新绘图。请参见下面的示例。当然,正常的matplotlib绘图(ax.plot(x,y))或上面讨论的seaborn轴水平函数的使用都可以很好地发挥作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
import seaborn as sns

tips = sns.load_dataset("tips")

class MainWindow(QtGui.QMainWindow):
    send_fig = QtCore.pyqtSignal(str)

    def __init__(self):
        super(MainWindow, self).__init__()

        self.main_widget = QtGui.QWidget(self)

        self.fig = Figure()
        self.ax1 = self.fig.add_subplot(121)
        self.ax2 = self.fig.add_subplot(122, sharex=self.ax1, sharey=self.ax1)
        self.axes=[self.ax1, self.ax2]
        self.canvas = FigureCanvas(self.fig)

        self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                  QtGui.QSizePolicy.Expanding)
        self.canvas.updateGeometry()

        self.dropdown1 = QtGui.QComboBox()
        self.dropdown1.addItems(["sex","time","smoker"])
        self.dropdown2 = QtGui.QComboBox()
        self.dropdown2.addItems(["sex","time","smoker","day"])
        self.dropdown2.setCurrentIndex(2)

        self.dropdown1.currentIndexChanged.connect(self.update)
        self.dropdown2.currentIndexChanged.connect(self.update)
        self.label = QtGui.QLabel("A plot:")

        self.layout = QtGui.QGridLayout(self.main_widget)
        self.layout.addWidget(QtGui.QLabel("Select category for subplots"))
        self.layout.addWidget(self.dropdown1)
        self.layout.addWidget(QtGui.QLabel("Select category for markers"))
        self.layout.addWidget(self.dropdown2)

        self.layout.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.show()
        self.update()

    def update(self):

        colors=["b","r","g","y","k","c"]
        self.ax1.clear()
        self.ax2.clear()
        cat1 = self.dropdown1.currentText()
        cat2 = self.dropdown2.currentText()
        print cat1, cat2

        for i, value in enumerate(tips[cat1].unique().get_values()):
            print"value", value
            df = tips.loc[tips[cat1] == value]
            self.axes[i].set_title(cat1 +":" + value)
            for j, value2 in enumerate(df[cat2].unique().get_values()):
                print"value2", value2
                df.loc[ tips[cat2] == value2 ].plot(kind="scatter", x="total_bill", y="tip",
                                                ax=self.axes[i], c=colors[j], label=value2)
        self.axes[i].legend()  
        self.fig.canvas.draw_idle()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    sys.exit(app.exec_())

enter

1
2
self.pgcanvas = pg.GraphicsLayoutWidget()
self.layout().addWidget(self.pgcanvas)

对于MatplotlibWidget(mw = pg.MatplotlibWidget())也是一样。尽管可以使用这种小部件,但它只是方便package,因为它所做的只是查找正确的matplotlib导入并创建FigureFigureCanvas实例。除非您正在使用其他pyqtgraph功能,否则导入完整的pyqtgraph程序包只是为了节省5行代码对我来说似乎有点过大。


这里是接受的答案的精确副本,但使用的是PYQT5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
import seaborn as sns

tips = sns.load_dataset("tips")

class MainWindow(QtWidgets.QMainWindow):
    send_fig = QtCore.pyqtSignal(str)

    def __init__(self):
        super(MainWindow, self).__init__()

        self.main_widget = QtWidgets.QWidget(self)

        self.fig = Figure()
        self.ax1 = self.fig.add_subplot(121)
        self.ax2 = self.fig.add_subplot(122, sharex=self.ax1, sharey=self.ax1)
        self.axes=[self.ax1, self.ax2]
        self.canvas = FigureCanvas(self.fig)

        self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding)
        self.canvas.updateGeometry()

        self.dropdown1 = QtWidgets.QComboBox()
        self.dropdown1.addItems(["sex","time","smoker"])
        self.dropdown2 = QtWidgets.QComboBox()
        self.dropdown2.addItems(["sex","time","smoker","day"])
        self.dropdown2.setCurrentIndex(2)

        self.dropdown1.currentIndexChanged.connect(self.update)
        self.dropdown2.currentIndexChanged.connect(self.update)
        self.label = QtWidgets.QLabel("A plot:")

        self.layout = QtWidgets.QGridLayout(self.main_widget)
        self.layout.addWidget(QtWidgets.QLabel("Select category for subplots"))
        self.layout.addWidget(self.dropdown1)
        self.layout.addWidget(QtWidgets.QLabel("Select category for markers"))
        self.layout.addWidget(self.dropdown2)

        self.layout.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.show()
        self.update()

    def update(self):

        colors=["b","r","g","y","k","c"]
        self.ax1.clear()
        self.ax2.clear()
        cat1 = self.dropdown1.currentText()
        cat2 = self.dropdown2.currentText()
        print (cat1, cat2)

        for i, value in enumerate(tips[cat1].unique().get_values()):
            print ("value", value)
            df = tips.loc[tips[cat1] == value]
            self.axes[i].set_title(cat1 +":" + value)
            for j, value2 in enumerate(df[cat2].unique().get_values()):
                print ("value2", value2)
                df.loc[ tips[cat2] == value2 ].plot(kind="scatter", x="total_bill", y="tip",
                                                ax=self.axes[i], c=colors[j], label=value2)
        self.axes[i].legend()  
        self.fig.canvas.draw_idle()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())

enter

请参阅此处:
https://yapayzekalabs.blogspot.com/2018/11/pyqt5-gui-qt-designer-matplotlib.html

这仅对您应遵循的图像和过程很有帮助。