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

Popular posts from this blog

c++ - Creating new partition disk winapi -

Android Prevent Bluetooth Pairing Dialog -

VBA function to include CDATA -