matplotlib 和 PyQt:动态图形在多次加载后运行缓慢或看起来很混乱



编辑:

决定重写它以包含我的问题的工作示例。虽然这很长,但我希望它在未来对许多人有用。

import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setGeometry(100, 100, 640, 480)
        showButton = QPushButton('Show')
        toolbarShowButton = self.addToolBar('Show button toolbar')
        toolbarShowButton.addWidget(showButton)
        self.connect(showButton, SIGNAL('clicked()'), self.showButtonClicked)
        self.graphLabel = GraphCanvas(self);
        self.setCentralWidget(self.graphLabel)
    def showButtonClicked(self):  
        self.graphLabel.drawGraph()
    def resizeEvent(self, event):
        try:
            self.graphLabel.setFig()
        except AttributeError:
            pass
class GraphCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.fig.add_subplot(111)
        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)
        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        self.background = None
    def drawGraph(self):
        self.axes.cla()
        self.someplot = self.axes.plot(range(1,5), range(1,5))
        self.redVert, = self.axes.plot(None, None, 'r--')
        self.greenVert, = self.axes.plot(None, None, 'g--')
        self.yellowVert, = self.axes.plot(None, None, 'y--')
        self.verticalLines = (self.redVert, self.greenVert, self.yellowVert)
        self.fig.canvas.mpl_connect('motion_notify_event', self.onMove)
        self.draw()
        self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)
    def onMove(self, event):
        # cursor moves on the canvas
        if event.inaxes:
            # restore the clean background
            self.fig.canvas.restore_region(self.background)
            ymin, ymax = self.axes.get_ylim()
            x = event.xdata - 1
            # draw each vertical line
            for line in self.verticalLines:
                line.set_xdata((x,))
                line.set_ydata((ymin, ymax))
                self.axes.draw_artist(line)
                x += 1
            self.fig.canvas.blit(self.axes.bbox)
    def setFig(self):
        '''
        Draws the canvas again after the main window
        has been resized.
        '''
        try:
            # hide all vertical lines
            for line in self.verticalLines:
                line.set_visible(False)
        except AttributeError:
            pass
        else:
            # draw canvas again and capture the background
            self.draw()
            self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)
            # set all vertical lines visible again
            for line in self.verticalLines:
                line.set_visible(True)
def main():
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())
if __name__ == '__main__': main()

代码说明

我有一个基本的QMainWindow,带有一个带有"显示"按钮的工具栏。主窗口还为matplotlib图形创建一个画布,并将其设置为中心小部件。

当用户点击"显示"按钮时,通过调用GraphCanvasdrawGraph()方法显示一些数据。在实际程序中,数据会根据用户在单击按钮之前选择显示的内容而变化。该方法基本上resizeEvent()再次绘制图形以适应新的中央小部件大小。

drawGraph()方法创建四个图,其中第一个图包含数据,因此它是可见的。最后两行绘制图形并将静态背景保存到变量self.background

当用户在画布上移动鼠标时,首先加载背景。我想保存并加载静态背景以使图形绘制更快。之后,最后三个图动态获取数据,并显示为 3 条随鼠标光标移动的垂直线。

问题所在

1(当您不断单击"显示"按钮时,图形会逐渐变慢。如果您尝试按 50 次并将鼠标移动到图形上,您会发现垂直线更加滞后。当真实图形中有更多动态绘图和注释等时,只需单击几下即可使用该程序。

更聪明的人可能会知道为什么会发生这种减速,但我想以前加载的数字保存在内存中的某个地方,并且可能绘制在新创建的图形下方。而且堆栈越来越大。

2(启动程序后立即显示该图。没什么大不了的,但我更喜欢那里只有一个空白区域,直到点击按钮。

尝试的解决方案

我尝试的是将这两行从class MainWindow def __init__(self)移动到def showButtonClicked(self)

self.graphLabel = GraphCanvas(self);
self.setCentralWidget(self.graphLabel)

所以现在看起来像这样:

def showButtonClicked(self):  
    self.graphLabel = GraphCanvas(self);
    self.setCentralWidget(self.graphLabel)
    self.graphLabel.drawGraph()

所以我只有在按下按钮后才创建了图形。这解决了减速问题,但带来了另一个问题。现在,当您点击"显示"按钮并在画布上移动鼠标时,已加载图形的保存背景,但原始尺寸为 5 x 4 英寸,我们一团糟。所以基本上背景不是以绘制图形的大小保存,而是以创建图形的大小保存。

但是,如果我调整窗口大小,一切都很好。但是下次我单击"显示"按钮时,问题再次出现,我需要再次调整窗口大小。

我需要什么

我需要让这个东西流畅地工作,并且无论单击"显示"按钮多少次,它都应该看起来。另外,我希望在第一次单击"显示"按钮之前不要显示该图形,并且从那时起直到程序关闭为止可见。

一些技巧来到了我的身边,比如在单击"显示"按钮时将窗口调整一个像素,但这不是正确的方法,不是吗?

欢迎任何想法和建议。谢谢。

我已经为这个问题找到了一个不错的解决方案,直到找到更好的解决方案。

我尝试的解决方案造成混乱的原因是,一旦创建了GraphCanvas的实例并将其设置为QCentralWidgetQCentralWidget就会缩小到GraphCanvas实例的大小,在这种情况下为 500*400,并保存该大小的Bbox。但是,图形本身使用整个可用空间。

当您创建GraphCanvas并设置为QCentralWidget时,小部件将使用GraphCanvas实例的大小,直到创建它的方法(及其所有父项(完成执行。之后他们都排好了队。

如果我们在 __init__ 方法中创建画布,它不会造成混乱,因为在 drawGraph 方法中,QCentralWidgetGraphCanvas 的大小匹配,并且使用了正确的bbox大小。但是,当我们在 showButtonClick 中创建它时,bbox将处于"错误"的大小,直到方法完成。

另外,如果我们在QMainWindow__init__方法中创建一个QCentralWidget,它的大小将与self.setGeometry()设置的整个窗口的大小相匹配。方法完成后,将计算QCentralWidget的大小以适应窗口,并且通常会变小。

为了解决这个问题,我决定在__init__方法中创建一个虚拟QWidget并将其添加为QCentralWidget。然后在showButtonClicked方法中,我获取QCentralWidget的宽度和高度,并使用保存的宽度和高度创建一个GraphCanvas。这样,尺寸从一开始就匹配。

所以这是相关的代码:

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setGeometry(100, 100, 640, 480)
        showButton = QPushButton('Show')
        toolbarShowButton = self.addToolBar('Show button toolbar')
        toolbarShowButton.addWidget(showButton)
        self.connect(showButton, SIGNAL('clicked()'), self.showButtonClicked)
        # dummy QWidget
        tempWidget = QWidget()
        self.setCentralWidget(tempWidget)
    def showButtonClicked(self):
        width = self.centralWidget().width()
        height = self.centralWidget().height()
        # convert to float (python 2) to prevent
        # flooring in the following divisions
        dpi = float(100)
        # create the canvas and replace the central widget
        self.graphLabel = GraphCanvas(self, width=width/dpi, height=height/dpi, dpi=dpi);
        self.setCentralWidget(self.graphLabel)
        self.graphLabel.drawGraph()

相关内容

  • 没有找到相关文章

最新更新