canvas - matplotlib and PyQt: Dynamic figure runs slow after several loads or looks messy -
edit:
i decided rewrite include working example of problem. although pretty long, hope proves useful many in future.
import sys matplotlib.backends.backend_qt4agg import figurecanvasqtagg figurecanvas matplotlib.figure import figure pyqt4.qtgui import * 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 canvas if event.inaxes: # restore clean background self.fig.canvas.restore_region(self.background) ymin, ymax = self.axes.get_ylim() x = event.xdata - 1 # draw each vertical line 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 canvas again after main window has been resized. ''' try: # hide vertical lines line in self.verticallines: line.set_visible(false) except attributeerror: pass else: # draw canvas again , capture background self.draw() self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox) # set vertical lines visible again 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()
description of code
i have basic qmainwindow
toolbar has "show" button. main window creates canvas matplotlib
figure , sets central widget.
when user hits "show" button, data shown calling drawgraph()
method of graphcanvas
. in real program data changes depending on user has selected shown prior clicking button. method resizeevent()
draws figure again accommodate new central widget size.
the drawgraph()
method creates 4 plots of first 1 has data, it's visible. last 2 lines draw figure , saves static background variable self.background
.
when user moves mouse on canvas, background first loaded. wanted save , load static background make figure draw faster. after that, last 3 plots data dynamically , shown 3 vertical lines move mouse cursor.
the problem
1) figure becomes gradually slower keep clicking "show" button. if try hitting 50 times , move mouse on figure, see vertical lines more laggy. when there's more dynamic plots , annotations etc. in real figure, program becomes unusable after few clicks.
someone wiser can tell why slowdown happening, guess loaded figures kept somewhere in memory , maybe drawn underneath newly created figure. , stack keeps getting bigger , bigger.
2) figure shown right after starting program. not huge deal, prefer blank area there until button clicked.
a solution tried
what tried moved these 2 lines def __init__(self)
of class mainwindow
def showbuttonclicked(self)
:
self.graphlabel = graphcanvas(self); self.setcentralwidget(self.graphlabel)
so looks this:
def showbuttonclicked(self): self.graphlabel = graphcanvas(self); self.setcentralwidget(self.graphlabel) self.graphlabel.drawgraph()
so created figure after button pressed. solves slowdown problem brings in problem. when hit "show" button , move mouse on canvas, saved background of figure loaded in original size of 5 4 inches , have mess. background saved not in size figure drawn, in size created in.
if resize window, however, works nicely. next time click "show" button, problem reappears , need resize window again.
what need
i need make thing work fluidly , should no matter how many times "show" button clicked. also, prefer if figure didn't show until "show" button clicked first time , point on visible until program closed.
a few hacks come mine resizing window 1 pixel when "show" button clicked, that's not right approach, it?
any ideas , suggestions more welcome. thank you.
i have found decent solution problem until better 1 found.
the reason why solution tried caused mess once instance of graphcanvas
created , set qcentralwidget
, qcentralwidget
shrinks size of graphcanvas
instance 500*400 in case, , bbox
of size saved. however, figure uses whole available space.
when create graphcanvas
, set qcentralwidget
, widget uses size of graphcanvas
instance until method created in (and parents) has finished executing. after both line up.
if create canvas in __init__
method, doesn't cause mess, because in drawgraph
method size of qcentralwidget
, graphcanvas
match , right bbox
size used. however, when create in showbuttonclick
, bbox
in 'wrong' size until method has finished.
in addition, if create qcentralwidget
in __init__
method of qmainwindow
, size of match size of whole window set self.setgeometry()
. after method finished, size of qcentralwidget
calculated fit in window , becomes smaller.
to solve problem, decided create dummy qwidget
in __init__
method , add qcentralwidget
. in showbuttonclicked
method grab width , height of qcentralwidget
, create graphcanvas
using saved width , height. way sizes match right beginning.
so here's relevant code:
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 float (python 2) prevent # flooring in following divisions dpi = float(100) # create canvas , replace central widget self.graphlabel = graphcanvas(self, width=width/dpi, height=height/dpi, dpi=dpi); self.setcentralwidget(self.graphlabel) self.graphlabel.drawgraph()
Comments
Post a Comment