2

编辑:

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

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并将其设置为中央小部件。

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

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

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

问题

1)随着您不断点击“显示”按钮,图形会逐渐变慢。如果您尝试敲击它 50 次并在图形上移动鼠标,您会发现垂直线更加滞后。当真实图形中有更多的动态图和注释等时,只需单击几下,程序就会变得无法使用。

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

2)启动程序后立即显示该图。没什么大不了的,但在单击按钮之前,我更喜欢那里的空白区域。

尝试了一个解决方案

我尝试的是将这两行从def __init__(self)ofclass MainWindow移到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 英寸,我们一团糟。所以基本上背景不是以绘制图形的大小保存的,而是以创建它的大小保存的。

但是,如果我调整窗口大小,一切正常。但是下次我单击“显示”按钮时,问题再次出现,我需要再次调整窗口大小。

我需要的

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

当单击“显示”按钮时,我会遇到一些技巧,例如将窗口大小调整一个像素,但这不是正确的方法,是吗?

任何想法和建议都非常受欢迎。谢谢你。

4

1 回答 1

1

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

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

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

如果我们在__init__方法中创建画布,它不会造成混乱,因为在drawGraph方法中使用了大小QCentralWidgetGraphCanvas匹配以及正确的bbox大小。但是,当我们在 中创建它时showButtonClickbbox在方法完成之前,它的大小将是“错误的”。

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

为了解决这个问题,我决定QWidget__init__方法中创建一个虚拟对象并将其添加为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()
于 2013-08-02T07:41:43.200 回答