Embedding “Figure Type” Seaborn Plot in PyQt (pyqtgraph)
我正在使用PyQt的package(pyqtgraph)来构建GUI应用程序。
我希望使用MatplotlibWidget在其中嵌入一个Seaborn图。但是,我的问题是,诸如
Seaborn的
但是,在GUI应用程序中,您很少要使用pyplot,而是要使用matplotlib API。
您在此处面临的问题是
创建自己的图形,因此最终得到两个图形。
现在,让我们退后一步:
原则上,可以通过在PyQt中使用海洋的
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中的绘图功能的,这些功能在图形级别起作用,例如
一个可行的解决方案是首先创建子图轴,然后再绘制到那些轴,例如使用pandas绘制功能。
1 | df.plot(kind="scatter", x=..., y=..., ax=...) |
其中
这允许在GUI中更新绘图。请参见下面的示例。当然,正常的matplotlib绘图(
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_()) |
关于pyqtgraph的最后一句话:我不会将pyqtgraph称为PyQt的package器,而是更多的扩展。尽管pyqtgraph附带了自己的Qt(这使它可移植并可以立即使用),但它也是一个可以从PyQt内部使用的软件包。因此,您可以通过
将
1 2 | self.pgcanvas = pg.GraphicsLayoutWidget() self.layout().addWidget(self.pgcanvas) |
对于MatplotlibWidget(
这里是接受的答案的精确副本,但使用的是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_()) |
虽然任何matplotlib图都可以以相同的方式嵌入pyqt5中,但要注意的是,随着数据集大小的增加,UI可能会变慢。但是我发现使用正则表达式功能可以方便地解析和绘制日志文件。
执行此操作的更好方法是设置"窗口小部件布局设置"文件,这样您可以用更少的代码来键入实际的主应用程序代码。
请参阅此处:
https://yapayzekalabs.blogspot.com/2018/11/pyqt5-gui-qt-designer-matplotlib.html
这仅对您应遵循的图像和过程很有帮助。