← Back to team overview

yade-dev team mailing list archive

live plot update during simulation

 

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
-- 
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 02:05:36 +0000
@@ -32,9 +32,12 @@
 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=[]
+plotFigs=[]
+
 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')
 
@@ -105,10 +108,12 @@
 		
 	to save the figure to file automatically.
 	"""
+	retPlots = []
 	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()
+		retPlots.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:
@@ -132,9 +137,95 @@
 			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 noShow: 
+		pylab.show()
+		return retPlots
+	else: 
+		return pylab.gcf() # return current figure
+
+def livePlotInit():
+	"""Prepare live plot
+	
+	"""
+	global plotFigs
+	global liveLines
+	plotFigs = []
+	liveLines = []
+	pylab.ion() ## # no interactive mode (hmmm, I don't know why actually...)
+	for p in plots:
+		p=p.strip()
+		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:
+			if d[0]=='|||' or d[0]==None:
+				y1=False; continue
+			if y1: plots_p_y1.append(d)
+			else: plots_p_y2.append(d)
+		#plotLines[p]=
+		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:
+			# try to move in the color palette a little further (magenta is 5th): r,g,b,c,m,y,k
+			origLinesColor=pylab.rcParams['lines.color']; pylab.rcParams['lines.color']='m'
+			# create the y2 axis
+			pylab.twinx()
+			#plotLines[p]+=
+			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'])
+
+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():
+	livePlotInit()
+	import thread
+	thread.start_new_thread(livePlotLoop,(1,2))
+
+
+def livePlotLoop(dummy1,dummy2):
+	import time
+	while True:
+		livePlotRefresh()
+		time.sleep(1)
 
 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 +246,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.")

=== modified file 'examples/concrete/uniax.py'
--- examples/concrete/uniax.py	2010-06-29 14:32:03 +0000
+++ examples/concrete/uniax.py	2010-07-06 02:15:01 +0000
@@ -176,5 +176,8 @@
 # sleep forever if run by yade-multi, exit is called from stopIfDamaged
 if os.environ.has_key('PARAM_TABLE'): time.sleep(1e12)
 
-
+# don't start the livePlot immediately because the autscale will be very bad
+import time
+time.sleep(2)
+yade.plot.livePlot()
 


Follow ups