仅在面板调整大小事件结束时重新绘制图表。

4

我目前正在尝试改进软件的绘图部分。

我们使用WXPython和Matplotlib来展示用户许多带有各种控件的图表。

总之,这就是背景:

Context

Matplotlib的性能对于我们的绘图需求来说还不错,但是调整wxpython主框架的大小会调整包含matplotlib画布的wx面板。

(在每个调整步骤中都会发生,而不仅仅是在最后)

调整面板很快,但是matplotlib也会在每个调整步骤中重新绘制画布和图形,导致视觉冻结和一些延迟。

总之,问题是:

enter image description here

我在想是否有一种方法可以在我们的WX Frame调整大小事件期间禁用(临时)matplotlib事件/自动重绘等内容,然后在其结束时应用重绘。 你有任何想法吗?
1个回答

3
这只是答案的一半,因为(1)我在这里使用PyQt而不是WX,(2)它仅显示如何防止调整大小,以便可以稍后手动完成。
思路是对FigureCanvas进行子类化并接管resizeEvent方法。 在此方法中,仅存储调整大小事件,但不要让其发生。
稍后手动触发该事件。
import sys
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np

class MyFigureCanvas(FigureCanvas):
    """ Subclass canvas to catch the resize event """
    def __init__(self, figure):
        self.lastEvent = False # store the last resize event's size here
        FigureCanvas.__init__(self, figure)

    def resizeEvent(self, event):
        if not self.lastEvent:
            # at the start of the app, allow resizing to happen.
            super(MyFigureCanvas, self).resizeEvent(event)
        # store the event size for later use
        self.lastEvent = (event.size().width(),event.size().height())
        print "try to resize, I don't let you."

    def do_resize_now(self):
        # recall last resize event's size
        newsize = QtCore.QSize(self.lastEvent[0],self.lastEvent[1] )
        # create new event from the stored size
        event = QtGui.QResizeEvent(newsize, QtCore.QSize(1, 1))
        print "Now I let you resize."
        # and propagate the event to let the canvas resize.
        super(MyFigureCanvas, self).resizeEvent(event)


class ApplicationWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.main_widget = QtGui.QWidget(self)
        l = QtGui.QVBoxLayout(self.main_widget)
        self.fig = Figure()
        # use subclassed FigureCanvas
        self.canvas = MyFigureCanvas(self.fig)
        self.button = QtGui.QPushButton("Manually Resize")        
        l.addWidget(self.button)
        l.addWidget(self.canvas)
        self.setCentralWidget(self.main_widget)
        self.button.clicked.connect(self.action)     
        self.plot()

    def action(self):
        # when button is clicked, resize the canvas.
        self.canvas.do_resize_now()

    def plot(self):
        # just some random plots
        self.axes = []
        for i in range(4):
            ax = self.fig.add_subplot(2,2,i+1)
            a = np.random.rand(100,100)
            ax.imshow(a)
            self.axes.append(ax)
        self.fig.tight_layout()


qApp = QtGui.QApplication(sys.argv)
aw = ApplicationWindow()
aw.show()
sys.exit(qApp.exec_())

这可能是在WX中进行此操作并没有太大区别。

不错的解决方案,但它没有解决重新调整大小后重绘图形的问题。不过,这可以通过安装一个eventFilter,如果event.type()MouseButtonReleaseNonClientAreaMouseButtonRelease,则运行 self.canvas.do_resize_now()来解决(灵感来自这个回答)。 - HelloGoodbye
如果事件类型为 WindowStateChange,也应该调用do_resize_now。此外,如果画布在 QDockWidget 中,可能需要将信号 visibilityChangeddockLocationChanged 连接到 do_resize_now - HelloGoodbye
此外,最好先检查画布的实际大小是否已通过将新大小与先前大小进行比较而调整大小,然后再调用 super(MyFigureCanvas, self).resizeEvent,否则在某些情况下,即使其大小未更改,图形也会被重新绘制。 - HelloGoodbye
最后,在某些情况下可能需要执行 QTimer.singleShot(0, do_resize_now),而不是直接调用do_resize_now(始终调用QTimer.singleShot并不会有影响),因为画布大小的更新可能已经排队在事件队列中。 - HelloGoodbye
@HelloGoodbye 这个答案使用按钮,必须按下才能触发重绘。如果您想自动重新绘制,所有这些点都适用。 - ImportanceOfBeingErnest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接