forked from qt-creator/qt-creator
Debugger: Replace gnuplot by python-matplotlib for graph dumper
Better functionality and availability than the gnuplot version. The feature ("Show array-like data in external plot window") will be automatically enabled if 'from matplotlib import pyplot' succeeds. Change-Id: I1799534ac5f878d3e43e47289d1b563b52bb4378 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -33,6 +33,8 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3:
|
||||||
xrange = range
|
xrange = range
|
||||||
@@ -72,19 +74,71 @@ SeparateLatin1StringFormat, \
|
|||||||
SeparateUtf8StringFormat \
|
SeparateUtf8StringFormat \
|
||||||
= range(100, 112)
|
= range(100, 112)
|
||||||
|
|
||||||
def hasPlot():
|
#
|
||||||
fileName = "/usr/bin/gnuplot"
|
# matplot based display for array-like structures.
|
||||||
return os.path.isfile(fileName) and os.access(fileName, os.X_OK)
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import subprocess
|
# FIXME: That might not be the one we want.
|
||||||
def arrayForms():
|
pythonExecutable = sys.executable
|
||||||
if hasPlot():
|
subprocess.check_call([pythonExecutable, '-c', 'import matplotlib'])
|
||||||
return "Normal,Plot"
|
hasPlot = True
|
||||||
return "Normal"
|
|
||||||
except:
|
except:
|
||||||
|
hasPlot = False
|
||||||
|
|
||||||
|
|
||||||
|
matplotFigure = {}
|
||||||
|
matplotCount = 0
|
||||||
|
|
||||||
|
matplotProc = subprocess.Popen(args=[pythonExecutable, "-i"],
|
||||||
|
bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
matplotProc.stdin.write(b"import sys\n")
|
||||||
|
matplotProc.stdin.write(b"sys.ps1=''\n")
|
||||||
|
matplotProc.stdin.write(b"from matplotlib import pyplot\n")
|
||||||
|
matplotProc.stdin.write(b"import time\n")
|
||||||
|
matplotProc.stdin.write(b"pyplot.ion()\n")
|
||||||
|
matplotProc.stdin.flush()
|
||||||
|
|
||||||
|
def matplotSend(iname, show, data):
|
||||||
|
global matplotFigure
|
||||||
|
global matplotCount
|
||||||
|
|
||||||
|
def s(line):
|
||||||
|
matplotProc.stdin.write(line.encode("latin1"))
|
||||||
|
matplotProc.stdin.write(b"\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
matplotProc.stdin.flush()
|
||||||
|
|
||||||
|
if show:
|
||||||
|
s("pyplot.ion()")
|
||||||
|
if not iname in matplotFigure:
|
||||||
|
matplotCount += 1
|
||||||
|
matplotFigure[iname] = matplotCount
|
||||||
|
s("pyplot.figure(%s)" % matplotFigure[iname])
|
||||||
|
s("pyplot.suptitle('%s')" % iname)
|
||||||
|
s("data = %s" % data)
|
||||||
|
s("pyplot.plot([i for i in range(len(data))], data, 'b.')")
|
||||||
|
time.sleep(0.2)
|
||||||
|
s("pyplot.draw()")
|
||||||
|
matplotProc.stdin.flush()
|
||||||
|
else:
|
||||||
|
if iname in matplotFigure:
|
||||||
|
s("pyplot.figure(%s)" % matplotFigure[iname])
|
||||||
|
s("pyplot.close()")
|
||||||
|
del matplotFigure[iname]
|
||||||
|
|
||||||
|
matplotProc.stdin.flush()
|
||||||
|
|
||||||
|
def matplotQuit():
|
||||||
|
matplotProc.stdin.write(b"exit")
|
||||||
|
matplotProc.kill()
|
||||||
|
|
||||||
def arrayForms():
|
def arrayForms():
|
||||||
return "Normal"
|
global hasPlot
|
||||||
|
return "Normal,Plot" if hasPlot else "Normal"
|
||||||
|
|
||||||
|
def mapForms():
|
||||||
|
return "Normal,Compact"
|
||||||
|
|
||||||
|
|
||||||
class ReportItem:
|
class ReportItem:
|
||||||
@@ -168,12 +222,6 @@ class Blob(object):
|
|||||||
def extractFloat(self, offset = 0):
|
def extractFloat(self, offset = 0):
|
||||||
return struct.unpack_from("f", self.data, offset)[0]
|
return struct.unpack_from("f", self.data, offset)[0]
|
||||||
|
|
||||||
#
|
|
||||||
# Gnuplot based display for array-like structures.
|
|
||||||
#
|
|
||||||
gnuplotPipe = {}
|
|
||||||
gnuplotPid = {}
|
|
||||||
|
|
||||||
def warn(message):
|
def warn(message):
|
||||||
print("XXX: %s\n" % message.encode("latin1"))
|
print("XXX: %s\n" % message.encode("latin1"))
|
||||||
|
|
||||||
@@ -1409,42 +1457,19 @@ class DumperBase:
|
|||||||
def putPlotData(self, base, n, typeobj, plotFormat = 2):
|
def putPlotData(self, base, n, typeobj, plotFormat = 2):
|
||||||
if self.isExpanded():
|
if self.isExpanded():
|
||||||
self.putArrayData(base, n, typeobj)
|
self.putArrayData(base, n, typeobj)
|
||||||
if not hasPlot():
|
if hasPlot:
|
||||||
return
|
if self.isSimpleType(typeobj):
|
||||||
if not self.isSimpleType(typeobj):
|
show = self.currentItemFormat() == plotFormat
|
||||||
|
iname = self.currentIName
|
||||||
|
data = []
|
||||||
|
if show:
|
||||||
|
base = self.createPointerValue(base, typeobj)
|
||||||
|
data = [str(base[i]) for i in range(0, toInteger(n))]
|
||||||
|
matplotSend(iname, show, data)
|
||||||
|
else:
|
||||||
#self.putValue(self.currentValue.value + " (not plottable)")
|
#self.putValue(self.currentValue.value + " (not plottable)")
|
||||||
self.putValue(self.currentValue.value)
|
self.putValue(self.currentValue.value)
|
||||||
self.putField("plottable", "0")
|
self.putField("plottable", "0")
|
||||||
return
|
|
||||||
global gnuplotPipe
|
|
||||||
global gnuplotPid
|
|
||||||
format = self.currentItemFormat()
|
|
||||||
iname = self.currentIName
|
|
||||||
if format != plotFormat:
|
|
||||||
if iname in gnuplotPipe:
|
|
||||||
os.kill(gnuplotPid[iname], 9)
|
|
||||||
del gnuplotPid[iname]
|
|
||||||
gnuplotPipe[iname].terminate()
|
|
||||||
del gnuplotPipe[iname]
|
|
||||||
return
|
|
||||||
base = self.createPointerValue(base, typeobj)
|
|
||||||
if not iname in gnuplotPipe:
|
|
||||||
gnuplotPipe[iname] = subprocess.Popen(["gnuplot"],
|
|
||||||
stdin=subprocess.PIPE)
|
|
||||||
gnuplotPid[iname] = gnuplotPipe[iname].pid
|
|
||||||
f = gnuplotPipe[iname].stdin;
|
|
||||||
# On Ubuntu install gnuplot-x11
|
|
||||||
f.write("set term wxt noraise\n")
|
|
||||||
f.write("set title 'Data fields'\n")
|
|
||||||
f.write("set xlabel 'Index'\n")
|
|
||||||
f.write("set ylabel 'Value'\n")
|
|
||||||
f.write("set grid\n")
|
|
||||||
f.write("set style data lines;\n")
|
|
||||||
f.write("plot '-' title '%s'\n" % iname)
|
|
||||||
for i in range(0, n):
|
|
||||||
f.write(" %s\n" % base.dereference())
|
|
||||||
base += 1
|
|
||||||
f.write("e\n")
|
|
||||||
|
|
||||||
def putSpecialArgv(self, value):
|
def putSpecialArgv(self, value):
|
||||||
"""
|
"""
|
||||||
@@ -1638,11 +1663,3 @@ DisplayUtf8String \
|
|||||||
= range(6)
|
= range(6)
|
||||||
|
|
||||||
|
|
||||||
def mapForms():
|
|
||||||
return "Normal,Compact"
|
|
||||||
|
|
||||||
def arrayForms():
|
|
||||||
if hasPlot():
|
|
||||||
return "Normal,Plot"
|
|
||||||
return "Normal"
|
|
||||||
|
|
||||||
|
@@ -2090,6 +2090,14 @@ def addExtraDumper(args):
|
|||||||
|
|
||||||
registerCommand("addExtraDumper", addExtraDumper)
|
registerCommand("addExtraDumper", addExtraDumper)
|
||||||
|
|
||||||
|
def exitGdb(arg):
|
||||||
|
if hasPlot:
|
||||||
|
matplotQuit()
|
||||||
|
gdb.execute("quit")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
registerCommand("exitGdb", exitGdb)
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
#
|
#
|
||||||
# Native Mixed
|
# Native Mixed
|
||||||
|
@@ -1922,7 +1922,7 @@ void GdbEngine::notifyAdapterShutdownOk()
|
|||||||
case QProcess::Running:
|
case QProcess::Running:
|
||||||
if (startParameters().closeMode == KillAndExitMonitorAtClose)
|
if (startParameters().closeMode == KillAndExitMonitorAtClose)
|
||||||
postCommand("monitor exit");
|
postCommand("monitor exit");
|
||||||
postCommand("-gdb-exit", GdbEngine::ExitRequest, CB(handleGdbExit));
|
postCommand("exitGdb", GdbEngine::ExitRequest, CB(handleGdbExit));
|
||||||
break;
|
break;
|
||||||
case QProcess::NotRunning:
|
case QProcess::NotRunning:
|
||||||
// Cannot find executable.
|
// Cannot find executable.
|
||||||
|
Reference in New Issue
Block a user