嵌入“数字型” Seaborn情节PyQt的(pyqtgraph)

问题描述 投票:2回答:2

我使用PyQt的(pyqtgraph)的包装,以构建一个GUI应用程序。我想用Seaborn内它嵌入MatplotlibWidget情节。然而,我的问题是,Seaborn包装方法如FacetGrid不接受外部数字手柄。此外,当我尝试更新MatplotlibWidget对象基本图(.fig)由FacetGrid它不工作(draw后无图)产生的身影。任何建议的解决方法吗?

matplotlib pyqt seaborn pyqtgraph matplotlib-widget
2个回答
5
投票

Seaborn的Facetgrid提供了一个方便的功能,熊猫dataframes快速连接到matplotlib pyplot接口。

然而,在GUI应用程序,你很少想使用pyplot,而是matplotlib API。

你面对这里的问题是,Facetgrid已经创建了自己的matplotlib.figure.Figure对象(Facetgrid.fig)。此外,MatplotlibWidget创建自己的人物,所以你最终有两个数字。

现在,让我们退一步位:原则上有可能在PyQt的使用seaborn Facetgrid情节,首先创建的情节,然后提供得出的数字,以图画布(matplotlib.backends.backend_qt4agg.FigureCanvasQTAgg)。以下是如何做到这一点的例子。

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内积有,这取决于用户交互beeing更新的目的。在上面的例子而言,这是非常低效的,因为它会要求创建一个新的数字实例,创建这个数字一个新的画布,并用新的替换旧的帆布情况下,将它添加到布局。

请注意,这个问题群是特定于seaborn这些绘图功能,在数字层面的工作,像lmplotfactorplotjointplotFacetGrid以及其它可能的。 像regplotboxplotkdeplot工作上​​的水平轴,并接受一个matplotlib axes对象作为参数(sns.regplot(x, y, ax=ax1))等功能。


甲不可能性的解决方案是先创建副区轴和后来情节这些轴线,例如使用pandas plotting functionality

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

其中ax应设置为先前创建的轴。 这允许更新GUI中的情节。请参见下面的例子。当然正常matplotlib绘制(ax.plot(x,y))或使用上述工作同样讨论的seaborn轴水平的功能。

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 image description here


A final word about pyqtgraph: I wouldn't call pyqtgraph a wrapper for PyQt but more an extention. Although pyqtgraph ships with its own Qt (which makes it portable and work out of the box), it is also a package one can use from within PyQt. You can therefore add a GraphicsLayoutWidget to a PyQt layout simply by
self.pgcanvas = pg.GraphicsLayoutWidget()
self.layout().addWidget(self.pgcanvas) 

这同样适用于一个MatplotlibWidgetmw = pg.MatplotlibWidget())。虽然你可以使用这种小部件,它的仅仅是一个方便的包装,因为所有它做的是找到正确的matplotlib进口和创造FigureFigureCanvas实例。除非你使用的是其他pyqtgraph功能,导入完整pyqtgraph包只是为了节省5行代码似乎有点矫枉过正了我。


-1
投票

有没有更好的办法做到这一点,是要建立一个“窗口小部件布局设置”文件,你可以更少的代码中输入您的实际主要的应用程序代码。

在这里看到:https://yapayzekalabs.blogspot.com/2018/11/pyqt5-gui-qt-designer-matplotlib.html

它有利于公正的形象和过程中,你应该遵循。

© www.soinside.com 2019 - 2024. All rights reserved.