编辑:
我决定重写它以包含我的问题的工作示例。虽然这很长,但我希望它在未来对许多人有用。
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
图形创建一个画布,并将其设置为中心小部件。
当用户点击"显示"按钮时,通过调用GraphCanvas
的drawGraph()
方法显示一些数据。在实际程序中,数据会根据用户在单击按钮之前选择显示的内容而变化。该方法基本上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
的实例并将其设置为QCentralWidget
,QCentralWidget
就会缩小到GraphCanvas
实例的大小,在这种情况下为 500*400,并保存该大小的Bbox
。但是,图形本身使用整个可用空间。
当您创建GraphCanvas
并设置为QCentralWidget
时,小部件将使用GraphCanvas
实例的大小,直到创建它的方法(及其所有父项(完成执行。之后他们都排好了队。
如果我们在 __init__
方法中创建画布,它不会造成混乱,因为在 drawGraph
方法中,QCentralWidget
和 GraphCanvas
的大小匹配,并且使用了正确的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()