yade-dev team mailing list archive
-
yade-dev team
-
Mailing list archive
-
Message #05089
Re: live plot update during simulation
-
To:
yade-dev@xxxxxxxxxxxxxxxxxxx
-
From:
Janek Kozicki <janek_listy@xxxxx>
-
Date:
Tue, 6 Jul 2010 05:04:34 +0200
-
Face:
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAALVBMVEUBAQEtLS1KSkpRUVFXV1dYWFhjY2Nzc3N3d3eHh4eKioqdnZ24uLjLy8vc3NxVIagyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2AIVEzgS1fgQtQAAAjRJREFUOMtt1DFv00AUAOAzFQNbjigSyoQaRaBMhKgLUyKXpVNNeUpk9vyDqFJhQ1kiBuaqAwJCqvPtSLY7RlTn5+5IdnYkkt/AOyfxXVLe5vf53Z1875kd34tOEax8djmj6GyjhB5bxz50GdsVZr9fqRjZwAtKOJw5Wqs2MMZ16ALHsaDncF7xAHix1oEFHAB8f+pRjcO4gfZDykcYzbiucRolOLUJ6kjA0xtVt+A6TySlM0RajIpK6DzwKZ/nOYbF/gclHMo1ZOHYY/+Ha+AWuM+3oMS4eeqYzZ8FiCltgUqI8cd2wwAVpJk+8LWYjBtnJdQpHQqJMd4Oxt4bU9ESiFGc5hkqaH74asAX4iabP5I5gZ+qjgGlJCqZa3h3lxhoeVcSE1qLQC4sqKOK9MGW9E3izFqqHokoztLFEgXg31sbZEKnWi2T74A4NxfVQqlkjKtcAWD+zcArFEES01dR0E/nnV0IgugmDd/2L84sOAouRBBHEc7gtc8teDkRlE0iNQPo2w3Xhh/D4TCIQ4LRLoTvgwjj6RRgavdurxYGMaIuGOyAW/PpNlCcU9/93AHenAWYjPoAwa+G3e3to/MgFNTAEKvKDjzuCzHTnY3qqdXtx24VijzQfZ0yewZ5cwRFQaa+mIYr1uI0I76+3W4xhlvoVRwOA0Fdl64HlJnxP6T8YpX/Lga4Wv4A3ErrU5oTfN7Mu/llXMl8RXEPji/lQkN3H7qXqgC2By47EXeU/7PJ/wPxRKMnuZwIeAAAAABJRU5ErkJggg==
-
In-reply-to:
<20100706043907.7c294f52@atak.bl.pg.gda.pl>
Janek Kozicki said: (by the date of Tue, 6 Jul 2010 04:39:07 +0200)
> Hello,
>
> I am learning python and there's a lot of stuff in python that I
> don't know yet. To learn a little bit more I decided to try adding a
> feature of showing live plot updates during simulation, like this:
>
> http://geo.hmg.inpg.fr/~janek/yade-videos/yade_livePlot.mpg
>
> https://www.yade-dem.org/wiki/Screenshots_and_videos#Experimantal_feature:_live_update_of_plotting
>
> By doing this I am sure that I broke some clean coding python
> conventions, just because I don't know python yet ;)
>
> And there is a very ugly trick involved, and I hope Vaclav that you
> could make this trick not ugly.
>
> The trick is following:
>
> wget http://tkinter.unpythonic.net/attach//mtTkinter/attachments/mtTkinter-0.3.tar.gz
> tar -xvzf ./mtTkinter-0.3.tar.gz
> sudo cp ./mtTkinter.py /usr/lib/python2.6/lib-tk/mtTkinter.py
> sudo vim /usr/lib/pymodules/python2.6/matplotlib/backends/tkagg.py
> :%s/import Tkinter as Tk/import mtTkinter as Tk/g
> :wq
>
> It is the ugliest thing that I have done to my debian installation recently.
>
> But after that? It works like a charm.
>
> Vaclav, I am not committing this, but rather sending as attachments.
> I prefer that you will review and clean it up.
>
> after applying those two patches you can just run uniax.py and enjoy
> live updating plot.
>
> best regards
there's a lot of duplicated code between plot(), livePlotInit() and livePlotUpdateData().
Both functions plot() and livePlotInit() could just call
livePlotUpdateData(). And perhaps livePlotInit() could be merged into
plot(). The only difference is that livePlotInit() is non-blocking,
while plot() is blocking by calling pylab.show()
OK.. in attached patch I have merged plot() and plotInit() into a
single function.
and also I added a delay in seconds. Now you can write
yade.plot.livePlot(0.1)
and see an updated plot 10 times per second!
--
Janek Kozicki http://janek.kozicki.pl/ |
=== modified file 'py/plot.py'
--- py/plot.py 2010-07-01 09:56:50 +0000
+++ py/plot.py 2010-07-06 03:01:36 +0000
@@ -31,10 +31,14 @@
"dictionary x-name -> (yspec,...), where yspec is either y-name or (y-name,'line-specification')"
labels={}
"Dictionary converting names in data to human-readable names (TeX names, for instance); if a variable is not specified, it is left untranslated."
+liveLines=[]
+"Used by livePlot() to store and refresh 'live' plot data. liveLines contains arrays of x:y points."
+plotFigs=[]
+"Used by livePlot() to store and refresh 'live' plot data. plotFigs is an array of pylab.figure() so that the opened windows can be refreshed."
def reset():
"Reset all plot-related variables (data, plots, labels)"
- global data, plots, labels # plotLines
+ global data, plots, labels, liveLines, plotFigs # plotLines
data={}; plots={}; # plotLines={};
pylab.close('all')
@@ -92,7 +96,7 @@
if l in labels.keys(): return labels[l]
else: return l
-def plot(noShow=False):
+def plot(noShow=False,livePlotInit=False):
"""Do the actual plot, which is either shown on screen (and nothing is returned: if *noShow* is False) or returned as object (if *noShow* is True).
You can use
@@ -102,13 +106,22 @@
>>> import os
>>> os.path.exists('someFile.pdf')
True
+
+ To have a live plot of the simulation use:
+
+ >>> yade.plot.livePlot()
to save the figure to file automatically.
"""
+ global plotFigs
+ global liveLines
+ plotFigs = []
+ liveLines = []
if not noShow: pylab.ion() ## # no interactive mode (hmmm, I don't know why actually...)
for p in plots:
p=p.strip()
- pylab.figure()
+ thisFig=pylab.figure()
+ plotFigs.append(thisFig)
plots_p=[_addPointTypeSpecifier(o) for o in _tuplifyYAxis(plots[p])]
plots_p_y1,plots_p_y2=[],[]; y1=True
for d in plots_p:
@@ -117,7 +130,8 @@
if y1: plots_p_y1.append(d)
else: plots_p_y2.append(d)
#plotLines[p]=
- pylab.plot(*sum([[data[p],data[d[0]],d[1]] for d in plots_p_y1],[]))
+ line, =pylab.plot(*sum([[data[p],data[d[0]],d[1]] for d in plots_p_y1],[]))
+ liveLines.append((line,p))
pylab.legend([_xlateLabel(_p[0]) for _p in plots_p_y1],loc=('upper left' if len(plots_p_y2)>0 else 'best'))
pylab.ylabel(','.join([_xlateLabel(_p[0]) for _p in plots_p_y1]))
if len(plots_p_y2)>0:
@@ -126,15 +140,67 @@
# create the y2 axis
pylab.twinx()
#plotLines[p]+=
- [pylab.plot(*sum([[data[p],data[d[0]],d[1]] for d in plots_p_y2],[]))]
+ line, =pylab.plot(*sum([[data[p],data[d[0]],d[1]] for d in plots_p_y2],[]))
+ liveLines.append((line,p))
pylab.legend([_xlateLabel(_p[0]) for _p in plots_p_y2],loc='upper right')
pylab.rcParams['lines.color']=origLinesColor
pylab.ylabel(','.join([_xlateLabel(_p[0]) for _p in plots_p_y2]))
pylab.xlabel(_xlateLabel(p))
if 'title' in O.tags.keys(): pylab.title(O.tags['title'])
- if not noShow: pylab.show()
- else: return pylab.gcf() # return current figure
-updatePeriod=0
+ if not livePlotInit: # block only if this is not a live plot
+ if not noShow:
+ pylab.show()
+ return retPlots
+ else:
+ return pylab.gcf() # return current figure
+
+def livePlotUpdateData():
+ global plotFigs
+ global liveLines
+ for (line,p) in liveLines:
+ p=p.strip()
+ plots_p=[_addPointTypeSpecifier(o) for o in _tuplifyYAxis(plots[p])]
+ plots_p_y1,plots_p_y2=[],[]; y1=True
+ for d in plots_p:
+ if d[0]=='|||' or d[0]==None:
+ y1=False; continue
+ if y1: plots_p_y1.append(d)
+ else: plots_p_y2.append(d)
+ # I don't know how to directly make a call to line.set_data, so I am using TMP_data
+ # for calling set_xdata() and set_ydata()
+ # but all those lines with TMP_data should be instead done by this single line below:
+ #line.set_data(*sum([[data[p],data[d[0]],d[1]] for d in plots_p_y1],[]))
+ TMP_data=sum([[data[p],data[d[0]],d[1]] for d in plots_p_y1],[])
+ line.set_ydata(TMP_data[1])
+ line.set_xdata(TMP_data[0])
+ if len(plots_p_y2)>0:
+ # same here, with TMP_data again...
+ #line.set_data(sum([[data[p],data[d[0]],d[1]] for d in plots_p_y2],[]))
+ TMP_data=sum([[data[p],data[d[0]],d[1]] for d in plots_p_y2],[])
+ line.set_ydata(TMP_data[1])
+ line.set_xdata(TMP_data[0])
+
+def livePlotRefresh():
+ livePlotUpdateData()
+ global plotFigs
+ for fig in plotFigs:
+ fig.canvas.draw()
+
+
+def livePlot(delay=1.):
+ plot(livePlotInit=True)
+ import thread
+ thread.start_new_thread(livePlotLoop,(delay,2))
+
+
+def livePlotLoop(delay,dummy2):
+ import time
+ while True:
+ try: # when simulation ends I got an error: 'xdata and ydata must be the same length'
+ livePlotRefresh()
+ time.sleep(delay)
+ except:
+ pass
def saveGnuplot(baseName,term='wxt',extension=None,timestamp=False,comment=None,title=None,varData=False):
"""Save data added with :yref:`yade.plot.addData` into (compressed) file and create .gnuplot file that attempts to mimick plots specified with :yref:`yade.plot.plots`.
@@ -155,7 +221,7 @@
comment:
a user comment (may be multiline) that will be embedded in the control file
-Returns name fo the gnuplot file created.
+Returns name for the gnuplot file created.
"""
import time,bz2
if len(data.keys())==0: raise RuntimeError("No data for plotting were saved.")
Follow ups
References