【问题标题】:Replot figure only at the end of a panel resize event仅在面板调整大小事件结束时重新绘制图形
【发布时间】:2017-03-02 12:43:26
【问题描述】:

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

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

总而言之,这里是上下文:

Matplotlib 的性能很好地满足了我们的绘图需求,但是,调整 wxpython 主框架的大小正在调整包含 matplotlib 画布的 wx 面板的大小。

(在每一步或调整大小时,而不仅仅是在最后)

如果快速调整面板大小,但 matplotlib 也在调整大小的每个步骤中重绘画布和图形,造成视觉冻结和一些滞后。

总结一下,问题:

我想知道是否有办法在我们的 WX Frame 调整大小事件期间禁用(临时)matplotlib 事件/自动重绘的东西,然后在它结束时应用重绘。

有什么想法吗?

【问题讨论】:

    标签: python performance matplotlib wxpython


    【解决方案1】:

    这只是答案的一半,因为 (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 中这样做并没有太大的不同。

    【讨论】:

    • 不错的解决方案,但它没有解决在调整大小结束时重绘绘图的问题。但是,如果event.type()MouseButtonReleaseNonClientAreaMouseButtonRelease(受this answer 启发),则可以通过installing 运行self.canvas.do_resize_now()eventFilter 来解决这个问题。
    • do_resize_now 如果事件是WindowStateChange 类型,也应该被调用。此外,如果画布在QDockWidget 中,则信号visibilityChangeddockLocationChanged 可能需要从connecteddo_resize_now
    • 此外,在调用 super(MyFigureCanvas, self).resizeEvent 之前,通过比较新尺寸和之前的尺寸来检查画布是否真的被调整了可能是个好主意,否则在某些情况下绘图会重绘,即使它的大小没有改变。
    • 最后,QTimer.singleShot(0, do_resize_now) 在某些情况下可能必须完成而不是直接调用do_resize_now(总是调用QTimer.singleShot 没有坏处),因为画布大小的更新可能已经在事件队列中排队。
    • @HelloGoodbye 这个答案使用了一个按钮,必须按下它才能触发重绘。相反,如果您想自动重绘它,那么所有这些点都确实适用。
    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 2021-01-10
    • 2023-04-02
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多