我正在使用PyQt的包装器(pyqtgraph)来构建GUI应用程序。我希望使用MatplotlibWidget在其中嵌入一个Seaborn图 。但是,我的问题是,诸如Seaborn的包装方法不接受外部图形处理。此外,当我尝试用产生的图形更新基于()的MatplotlibWidget对象时,它不起作用(后面没有图形)。有任何解决方法的建议吗?FacetGrid
.fig
FacetGrid
draw
Seaborn Facetgrid
提供了一种便捷功能,可将熊猫数据帧快速连接到matplotlib pyplot接口。
但是,在GUI应用程序中,您很少要使用pyplot,而是要使用matplotlib API。
您在此处面临的问题是Facetgrid
已经创建了自己的matplotlib.figure.Figure
对象(Facetgrid.fig
)。此外,MatplotlibWidget创建自己的图形,因此最终得到两个图形。
现在,让我们退后一步:原则上,可以通过Facetgrid
在PyQt中使用Seaborn 图,首先创建图,然后将结果图提供给图画布(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内创建图的目的是根据用户的交互来更新信息。在上面的示例中,这效率很低,因为它需要创建一个新的图形实例,用该图形创建一个新的画布,并用新的画布实例替换旧的画布实例,然后将其添加到布局中。
请注意,这个问题群是特定于seaborn这些绘图功能,在数字层面的工作,如lmplot
,factorplot
,jointplot
,FacetGrid
以及其它可能的。
其他的功能,如regplot
,boxplot
,kdeplot
上轴水平工作,并接受一个matplotlib axes
对象作为参数(sns.regplot(x, y, ax=ax1)
)。
一种可行的解决方案是首先创建子图轴,然后再绘制到那些轴,例如使用pandas绘制功能。
df.plot(kind="scatter", x=..., y=..., ax=...)
ax
应该在哪里设置为先前创建的轴。
这允许在GUI中更新绘图。请参见下面的示例。当然,正常的matplotlib绘图(ax.plot(x,y)
)或上面讨论的seaborn axis level函数的使用也同样有效。
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_())
GraphicsLayoutWidget
简单地通过以下方式向PyQt布局添加一个
self.pgcanvas = pg.GraphicsLayoutWidget() self.layout().addWidget(self.pgcanvas)
这同样适用于一个MatplotlibWidget(mw = pg.MatplotlibWidget()
)。尽管可以使用这种小部件,但它只是一个方便包装,因为它所做的只是查找正确的matplotlib导入并创建Figure
和FigureCanvas
实例。除非您正在使用其他pyqtgraph功能,否则导入完整的pyqtgraph软件包只是为了节省5行代码,对我来说似乎有点过头了。