summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhjk <hjk@theqtcompany.com>2015-04-01 17:19:43 +0200
committerhjk <hjk@theqtcompany.com>2015-04-02 12:25:04 +0000
commit3658bdac898c94a10eef2f04546c459eab424a5c (patch)
treea1c35d92930ada15a26f67a74a98d64cbd15d387
parent3e82dcad4435a9c0747f6880aaa7e0a968156780 (diff)
downloadqt-creator-3658bdac898c94a10eef2f04546c459eab424a5c.tar.gz
Debugger: Use primitive internal widget instead of matplotview
This practically removes any functionality beyond plain plot display, but does that at least reliably, cross-platform, without dependency on 3rd party python packages. Change-Id: Iaff2f78595394522f32264c642df20dd48b83f8b Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
-rw-r--r--share/qtcreator/debugger/dumper.py249
-rw-r--r--share/qtcreator/debugger/gdbbridge.py2
-rw-r--r--share/qtcreator/debugger/qttypes.py17
-rw-r--r--share/qtcreator/debugger/stdtypes.py8
-rw-r--r--src/plugins/debugger/debuggerprotocol.h3
-rw-r--r--src/plugins/debugger/imageviewer.cpp85
-rw-r--r--src/plugins/debugger/imageviewer.h25
-rw-r--r--src/plugins/debugger/watchdata.cpp57
-rw-r--r--src/plugins/debugger/watchdata.h6
-rw-r--r--src/plugins/debugger/watchhandler.cpp202
10 files changed, 357 insertions, 297 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py
index 584c35e138..78f48db03d 100644
--- a/share/qtcreator/debugger/dumper.py
+++ b/share/qtcreator/debugger/dumper.py
@@ -36,13 +36,6 @@ import re
import time
import importlib
-try:
- import subprocess
- hasSubprocess = True
-except:
- hasSubprocess = False
- hasPlot = False
-
if sys.version_info[0] >= 3:
xrange = range
toInteger = int
@@ -105,81 +98,53 @@ WatchpointAtExpression, \
BreakpointOnQmlSignalEmit, \
BreakpointAtJavaScriptThrow, \
= range(0, 14)
-#
-# matplot based display for array-like structures.
-#
-try:
- import matplotlib
- hasPlot = True
-except:
- hasPlot = False
-
-if hasSubprocess and hasPlot:
- matplotFigure = {}
- matplotCount = 0
- matplotProc = None
- devNull = None
-
- def matplotInit():
- global matplotProc
- global devNull
-
- if matplotProc is None:
- devNull = open(os.devnull)
- # FIXME: That might not be the one we want.
- pythonExecutable = sys.executable
- matplotProc = subprocess.Popen(args=[pythonExecutable, "-i"],
- bufsize=0, stdin=subprocess.PIPE, stdout=devNull, stderr=devNull)
-
- 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
-
- matplotInit()
-
- 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()
+# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
+Unencoded8Bit, \
+Base64Encoded8BitWithQuotes, \
+Base64Encoded16BitWithQuotes, \
+Base64Encoded32BitWithQuotes, \
+Base64Encoded16Bit, \
+Base64Encoded8Bit, \
+Hex2EncodedLatin1, \
+Hex4EncodedLittleEndian, \
+Hex8EncodedLittleEndian, \
+Hex2EncodedUtf8, \
+Hex8EncodedBigEndian, \
+Hex4EncodedBigEndian, \
+Hex4EncodedLittleEndianWithoutQuotes, \
+Hex2EncodedLocal8Bit, \
+JulianDate, \
+MillisecondsSinceMidnight, \
+JulianDateAndMillisecondsSinceMidnight, \
+Hex2EncodedInt1, \
+Hex2EncodedInt2, \
+Hex2EncodedInt4, \
+Hex2EncodedInt8, \
+Hex2EncodedUInt1, \
+Hex2EncodedUInt2, \
+Hex2EncodedUInt4, \
+Hex2EncodedUInt8, \
+Hex2EncodedFloat4, \
+Hex2EncodedFloat8, \
+IPv6AddressAndHexScopeId, \
+Hex2EncodedUtf8WithoutQuotes, \
+DateTimeInternal \
+ = range(30)
+
+# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h
+StopDisplay, \
+DisplayImageData, \
+DisplayUtf16String, \
+DisplayImageFile, \
+DisplayLatin1String, \
+DisplayUtf8String, \
+DisplayPlotData \
+ = range(7)
- def matplotQuit():
- global matplotProc
- if not matplotProc is None:
- matplotProc.stdin.write(b"exit")
- matplotProc.kill()
- devNull.close()
def arrayForms():
- global hasPlot
- return [ArrayPlotFormat] if hasPlot else []
+ return [ArrayPlotFormat]
def mapForms():
return [CompactMapFormat]
@@ -612,10 +577,7 @@ class DumperBase:
self.putNumChild(0)
self.putValue(mem, encodingType, elided=elided)
- if displayFormat == Latin1StringFormat \
- or displayFormat == Utf8StringFormat:
- self.putDisplay(StopDisplay)
- elif displayFormat == SeparateLatin1StringFormat \
+ if displayFormat == SeparateLatin1StringFormat \
or displayFormat == SeparateUtf8StringFormat:
self.putField("editformat", displayType)
elided, shown = self.computeLimit(bytelen, 100000)
@@ -916,14 +878,14 @@ class DumperBase:
def putCStyleArray(self, value):
arrayType = value.type.unqualified()
innerType = value[0].type
+ innerTypeName = str(innerType.unqualified())
ts = innerType.sizeof
- #self.putAddress(value.address)
+
try:
self.putValue("@0x%x" % self.addressOf(value), priority = -1)
except:
self.putEmptyValue()
self.putType(arrayType)
- self.putNumChild(1)
try:
p = self.addressOf(value)
@@ -933,20 +895,20 @@ class DumperBase:
displayFormat = self.currentItemFormat()
n = int(arrayType.sizeof / ts)
- if p and self.tryPutSimpleFormattedPointer(p, str(arrayType), displayFormat, arrayType.sizeof):
- self.putNumChild(n)
- pass
- elif displayFormat is None:
- innerTypeName = str(innerType.unqualified())
- blob = self.readMemory(self.addressOf(value), arrayType.sizeof)
+ if displayFormat != RawFormat:
if innerTypeName == "char":
# Use Latin1 as default for char [].
+ blob = self.readMemory(self.addressOf(value), arrayType.sizeof)
self.putValue(blob, Hex2EncodedLatin1)
elif innerTypeName == "wchar_t":
+ blob = self.readMemory(self.addressOf(value), arrayType.sizeof)
if innerType.sizeof == 2:
self.putValue(blob, Hex4EncodedLittleEndian)
else:
self.putValue(blob, Hex8EncodedLittleEndian)
+ elif p:
+ self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName, displayFormat, arrayType.sizeof)
+ self.putNumChild(n)
if self.isExpanded():
try:
@@ -958,17 +920,7 @@ class DumperBase:
with Children(self, childType=innerType):
self.putFields(value)
- if hasPlot and self.isSimpleType(innerType):
- show = displayFormat == ArrayPlotFormat
- iname = self.currentIName
- data = []
- if show:
- base = self.createPointerValue(p, innerType)
- data = [str(base[i]) for i in range(0, n)]
- matplotSend(iname, show, data)
- else:
- #self.putValue(self.currentValue.value + " (not plottable)")
- self.putField("plottable", "0")
+ self.putPlotDataHelper(p, n, innerType)
def cleanAddress(self, addr):
if addr is None:
@@ -1038,29 +990,23 @@ class DumperBase:
data = self.readMemory(base, shown)
self.putValue(data, Hex2EncodedLatin1, elided=elided)
- def putDisplay(self, editFormat, value = None, cmd = None):
+ def putDisplay(self, editFormat, value):
self.put('editformat="%s",' % editFormat)
- if cmd is None:
- if not value is None:
- self.put('editvalue="%s",' % value)
- else:
- self.put('editvalue="%s|%s",' % (cmd, value))
+ self.put('editvalue="%s",' % value)
# This is shared by pointer and array formatting.
- def tryPutSimpleFormattedPointer(self, value, typeName, displayFormat, limit):
- if displayFormat == AutomaticFormat and typeName == "char":
+ def tryPutSimpleFormattedPointer(self, value, typeName, innerTypeName, displayFormat, limit):
+ if displayFormat == AutomaticFormat and innerTypeName == "char":
# Use Latin1 as default for char *.
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 1, limit)
self.putValue(data, Hex2EncodedLatin1, elided=elided)
- self.putDisplay(StopDisplay)
return True
if displayFormat == Latin1StringFormat:
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 1, limit)
self.putValue(data, Hex2EncodedLatin1, elided=elided)
- self.putDisplay(StopDisplay)
return True
if displayFormat == SeparateLatin1StringFormat:
@@ -1074,7 +1020,6 @@ class DumperBase:
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 1, limit)
self.putValue(data, Hex2EncodedUtf8, elided=elided)
- self.putDisplay(StopDisplay)
return True
if displayFormat == SeparateUtf8StringFormat:
@@ -1088,21 +1033,18 @@ class DumperBase:
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 1, limit)
self.putValue(data, Hex2EncodedLocal8Bit, elided=elided)
- self.putDisplay(StopDisplay)
return True
if displayFormat == Utf16StringFormat:
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 2, limit)
self.putValue(data, Hex4EncodedLittleEndian, elided=elided)
- self.putDisplay(StopDisplay)
return True
if displayFormat == Ucs4StringFormat:
self.putType(typeName)
(elided, data) = self.encodeCArray(value, 4, limit)
self.putValue(data, Hex8EncodedLittleEndian, elided=elided)
- self.putDisplay(StopDisplay)
return True
return False
@@ -1155,7 +1097,7 @@ class DumperBase:
if displayFormat == SeparateLatin1StringFormat \
or displayFormat == SeparateUtf8StringFormat:
limit = 1000000
- if self.tryPutSimpleFormattedPointer(value, typeName, displayFormat, limit):
+ if self.tryPutSimpleFormattedPointer(value, typeName, innerTypeName, displayFormat, limit):
self.putNumChild(0)
return
@@ -1555,22 +1497,18 @@ class DumperBase:
self.putArrayData(addr, n, self.lookupType(typeName))
self.putAddress(addr)
- def putPlotData(self, base, n, typeobj):
+ def putPlotDataHelper(self, base, n, innerType):
+ if self.currentItemFormat() == ArrayPlotFormat and self.isSimpleType(innerType):
+ enc = self.simpleEncoding(innerType)
+ if enc:
+ self.putField("editencoding", enc)
+ self.putField("editvalue", self.readMemory(base, n * innerType.sizeof))
+ self.putField("editformat", DisplayPlotData)
+
+ def putPlotData(self, base, n, innerType):
+ self.putPlotDataHelper(base, n, innerType)
if self.isExpanded():
- self.putArrayData(base, n, typeobj)
- if hasPlot:
- if self.isSimpleType(typeobj):
- show = self.currentItemFormat() == ArrayPlotFormat
- 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)
- self.putField("plottable", "0")
+ self.putArrayData(base, n, innerType)
def putSpecialArgv(self, value):
"""
@@ -1757,10 +1695,6 @@ class DumperBase:
self.qqEditable = {}
self.typeCache = {}
- if hasPlot: # Hack for generic array type. [] is used as "type" name.
- self.qqDumpers['[]'] = ""
- self.qqFormats['[]'] = arrayForms()
-
for mod in self.dumpermodules:
m = importlib.import_module(mod)
dic = m.__dict__
@@ -1981,48 +1915,3 @@ class DumperBase:
return items
-# Some "Enums"
-
-# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
-Unencoded8Bit, \
-Base64Encoded8BitWithQuotes, \
-Base64Encoded16BitWithQuotes, \
-Base64Encoded32BitWithQuotes, \
-Base64Encoded16Bit, \
-Base64Encoded8Bit, \
-Hex2EncodedLatin1, \
-Hex4EncodedLittleEndian, \
-Hex8EncodedLittleEndian, \
-Hex2EncodedUtf8, \
-Hex8EncodedBigEndian, \
-Hex4EncodedBigEndian, \
-Hex4EncodedLittleEndianWithoutQuotes, \
-Hex2EncodedLocal8Bit, \
-JulianDate, \
-MillisecondsSinceMidnight, \
-JulianDateAndMillisecondsSinceMidnight, \
-Hex2EncodedInt1, \
-Hex2EncodedInt2, \
-Hex2EncodedInt4, \
-Hex2EncodedInt8, \
-Hex2EncodedUInt1, \
-Hex2EncodedUInt2, \
-Hex2EncodedUInt4, \
-Hex2EncodedUInt8, \
-Hex2EncodedFloat4, \
-Hex2EncodedFloat8, \
-IPv6AddressAndHexScopeId, \
-Hex2EncodedUtf8WithoutQuotes, \
-DateTimeInternal \
- = range(30)
-
-# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h
-StopDisplay, \
-DisplayImageData, \
-DisplayUtf16String, \
-DisplayImageFile, \
-DisplayLatin1String, \
-DisplayUtf8String \
- = range(6)
-
-
diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py
index e8f07d68f3..06ec795447 100644
--- a/share/qtcreator/debugger/gdbbridge.py
+++ b/share/qtcreator/debugger/gdbbridge.py
@@ -1615,8 +1615,6 @@ class Dumper(DumperBase):
self.qmlBreakpoints.append(Resolver(self, args))
def exitGdb(self, _):
- if hasPlot:
- matplotQuit()
gdb.execute("quit")
def loadDumpers(self, args):
diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py
index 59a0829d9d..8323314d34 100644
--- a/share/qtcreator/debugger/qttypes.py
+++ b/share/qtcreator/debugger/qttypes.py
@@ -62,14 +62,12 @@ def qdump__QByteArray(d, value):
elided, p = d.encodeByteArrayHelper(d.extractPointer(value), d.displayStringLimit)
displayFormat = d.currentItemFormat()
if displayFormat == AutomaticFormat or displayFormat == Latin1StringFormat:
- d.putDisplay(StopDisplay)
d.putValue(p, Hex2EncodedLatin1, elided=elided)
elif displayFormat == SeparateLatin1StringFormat:
d.putValue(p, Hex2EncodedLatin1, elided=elided)
d.putField("editformat", DisplayLatin1String)
d.putField("editvalue", d.encodeByteArray(value, limit=100000))
elif displayFormat == Utf8StringFormat:
- d.putDisplay(StopDisplay)
d.putValue(p, Hex2EncodedUtf8, elided=elided)
elif displayFormat == SeparateUtf8StringFormat:
d.putValue(p, Hex2EncodedUtf8, elided=elided)
@@ -546,8 +544,7 @@ def qdump__QFiniteStack(d, value):
size = int(value["_size"])
d.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
d.putItemCount(size)
- if d.isExpanded():
- d.putPlotData(value["_array"], size, d.templateArgument(value.type, 0))
+ d.putPlotData(value["_array"], size, d.templateArgument(value.type, 0))
def qdump__QFlags(d, value):
@@ -847,9 +844,7 @@ def qdump__QImage(d, value):
d.putType("void *")
displayFormat = d.currentItemFormat()
- if displayFormat == SimpleFormat:
- d.putDisplay(StopDisplay)
- elif displayFormat == SeparateFormat:
+ if displayFormat == SeparateFormat:
# This is critical for performance. Writing to an external
# file using the following is faster when using GDB.
# file = tempfile.mkstemp(prefix="gdbpy_")
@@ -1743,9 +1738,7 @@ def qdump__QString(d, value):
data, size, alloc = d.stringData(value)
d.putNumChild(size)
displayFormat = d.currentItemFormat()
- if displayFormat == SimpleFormat:
- d.putDisplay(StopDisplay)
- elif displayFormat == SeparateFormat:
+ if displayFormat == SeparateFormat:
d.putField("editformat", DisplayUtf16String)
d.putField("editvalue", d.encodeString(value, limit=100000))
if d.isExpanded():
@@ -1897,9 +1890,7 @@ def qdump__QUrl(d, value):
d.putValue(url, Hex4EncodedLittleEndian)
displayFormat = d.currentItemFormat()
- if displayFormat == SimpleFormat:
- d.putDisplay(StopDisplay)
- elif displayFormat == SeparateFormat:
+ if displayFormat == SeparateFormat:
d.putField("editformat", DisplayUtf16String)
d.putField("editvalue", url)
diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py
index 54a6cc34d7..eb34ac4159 100644
--- a/share/qtcreator/debugger/stdtypes.py
+++ b/share/qtcreator/debugger/stdtypes.py
@@ -711,15 +711,15 @@ def qdump__std__vector(d, value):
d.checkPointer(alloc)
d.putItemCount(size)
- if d.isExpanded():
- if isBool:
+ if isBool:
+ if d.isExpanded():
with Children(d, size, maxNumChild=10000, childType=type):
base = d.pointerValue(start)
for i in d.childRange():
q = base + int(i / 8)
d.putBoolItem(str(i), (int(d.extractPointer(q)) >> (i % 8)) & 1)
- else:
- d.putPlotData(start, size, type)
+ else:
+ d.putPlotData(start, size, type)
def qdump__std__vector__QNX(d, value):
innerType = d.templateArgument(value.type, 0)
diff --git a/src/plugins/debugger/debuggerprotocol.h b/src/plugins/debugger/debuggerprotocol.h
index 3214fdb76e..7bccb4356b 100644
--- a/src/plugins/debugger/debuggerprotocol.h
+++ b/src/plugins/debugger/debuggerprotocol.h
@@ -246,7 +246,8 @@ enum DebuggerDisplay {
DisplayUtf16String = 2,
DisplayImageFile = 3,
DisplayLatin1String = 4,
- DisplayUtf8String = 5
+ DisplayUtf8String = 5,
+ DisplayPlotData = 6
};
// Decode string data as returned by the dumper helpers.
QString decodeData(const QByteArray &baIn, int encoding);
diff --git a/src/plugins/debugger/imageviewer.cpp b/src/plugins/debugger/imageviewer.cpp
index 898ef7069a..cc3a614124 100644
--- a/src/plugins/debugger/imageviewer.cpp
+++ b/src/plugins/debugger/imageviewer.cpp
@@ -114,11 +114,15 @@ ImageViewer::ImageViewer(QWidget *parent)
connect(m_imageWidget, &ImageWidget::clicked, this, &ImageViewer::clicked);
}
-void ImageViewer::setImage(const QImage &i)
+void ImageViewer::setImage(const QImage &image)
{
- m_imageWidget->setImage(i);
- m_info = tr("Size: %1x%2, %3 byte, format: %4, depth: %5")
- .arg(i.width()).arg(i.height()).arg(i.byteCount()).arg(i.format()).arg(i.depth());
+ m_imageWidget->setImage(image);
+ clicked(QString());
+}
+
+void ImageViewer::setInfo(const QString &info)
+{
+ m_info = info;
clicked(QString());
}
@@ -165,4 +169,77 @@ void ImageViewer::contextMenuEvent(QContextMenuEvent *ev)
openImageViewer(image);
}
+
+//
+//
+//
+
+PlotViewer::PlotViewer(QWidget *parent)
+ : QWidget(parent)
+{
+}
+
+void PlotViewer::setData(const PlotViewer::Data &data)
+{
+ m_data = data;
+ update();
+}
+
+void PlotViewer::setInfo(const QString &description)
+{
+ m_info = description;
+ update();
+}
+
+void PlotViewer::paintEvent(QPaintEvent *)
+{
+ QPainter pain(this);
+
+ const int n = int(m_data.size());
+ const int w = width();
+ const int h = height();
+ const int b = 10; // Border width.
+
+ pain.fillRect(rect(), Qt::white);
+
+ double ymin = 0;
+ double ymax = 0;
+ for (int i = 0; i < n; ++i) {
+ const double v = m_data.at(i);
+ if (v < ymin)
+ ymin = v;
+ else if (v > ymax)
+ ymax = v;
+ }
+
+ const double d = ymin == ymax ? (h / 2 - b) : (ymax - ymin);
+ const int k = 1; // Length of cross marker arms.
+
+ for (int i = 0; i + 1 < n; ++i) {
+ // Lines between points.
+ const int x1 = b + i * (w - 2 * b) / (n - 1);
+ const int x2 = b + (i + 1) * (w - 2 * b) / (n - 1);
+ const int y1 = h - (b + int((m_data[i] - ymin) * (h - 2 * b) / d));
+ const int y2 = h - (b + int((m_data[i + 1] - ymin) * (h - 2 * b) / d));
+ pain.drawLine(x1, y1, x2, y2);
+
+ if (i == 0) {
+ // Little cross marker on first point
+ pain.drawLine(x1 - k, y1 - k, x1 + k, y1 + k);
+ pain.drawLine(x1 + k, y1 - k, x1 - k, y1 + k);
+ }
+ // ... and all subsequent points.
+ pain.drawLine(x2 - k, y2 - k, x2 + k, y2 + k);
+ pain.drawLine(x2 + k, y2 - k, x2 - k, y2 + k);
+ }
+
+ if (n) {
+ pain.drawText(10, 10,
+ QString::fromLatin1("%5 items. X: %1..%2, Y: %3...%4").arg(0).arg(n).arg(ymin).arg(ymax).arg(n));
+ } else {
+ pain.drawText(10, 10,
+ QString::fromLatin1("Container is empty"));
+ }
+}
+
#include "imageviewer.moc"
diff --git a/src/plugins/debugger/imageviewer.h b/src/plugins/debugger/imageviewer.h
index 668453daa5..5ddee6f022 100644
--- a/src/plugins/debugger/imageviewer.h
+++ b/src/plugins/debugger/imageviewer.h
@@ -33,6 +33,8 @@
#include <QWidget>
+#include <vector>
+
QT_BEGIN_NAMESPACE
class QScrollArea;
class QLabel;
@@ -49,19 +51,36 @@ class ImageViewer : public QWidget
public:
explicit ImageViewer(QWidget *parent = 0);
- void setImage(const QImage &);
+ void setImage(const QImage &image);
+ void setInfo(const QString &description);
protected:
void contextMenuEvent(QContextMenuEvent *);
-private slots:
+private:
void clicked(const QString &);
-private:
QScrollArea *m_scrollArea;
ImageWidget *m_imageWidget;
QLabel *m_infoLabel;
QString m_info;
};
+class PlotViewer : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit PlotViewer(QWidget *parent = 0);
+
+ typedef std::vector<double> Data;
+ void setData(const Data &data);
+ void setInfo(const QString &description);
+
+ void paintEvent(QPaintEvent *ev);
+
+private:
+ Data m_data;
+ QString m_info;
+};
+
#endif // IMAGEVIEWER_H
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index 098051d9e9..6d46835227 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -122,6 +122,7 @@ WatchData::WatchData() :
id(0),
state(InitialState),
editformat(StopDisplay),
+ editencoding(Unencoded8Bit),
address(0),
origaddr(0),
size(0),
@@ -552,14 +553,12 @@ void parseChildrenData(const WatchData &data0, const GdbMi &item,
GdbMi children = item["children"];
data.updateType(item["type"]);
- GdbMi mi = item["editvalue"];
- if (mi.isValid())
- data.editvalue = mi.data();
- mi = item["editformat"];
- data.editformat = DebuggerDisplay(mi.toInt());
+ data.editvalue = item["editvalue"].data();
+ data.editformat = DebuggerDisplay(item["editformat"].toInt());
+ data.editencoding = DebuggerEncoding(item["editencoding"].toInt());
- mi = item["valueelided"];
+ GdbMi mi = item["valueelided"];
if (mi.isValid())
data.elided = mi.toInt();
@@ -658,6 +657,52 @@ void parseWatchData(const WatchData &data0, const GdbMi &input,
parseChildrenData(data0, input, itemHandler, childHandler, arrayDecoder);
}
+template <class T>
+void readNumericVectorHelper(std::vector<double> *v, const QByteArray &ba)
+{
+ const T *p = (const T *) ba.data();
+ std::copy(p, p + ba.size() / sizeof(T), std::back_insert_iterator<std::vector<double> >(*v));
+}
+
+void readNumericVector(std::vector<double> *v, const QByteArray &rawData, DebuggerEncoding encoding)
+{
+ switch (encoding) {
+ case Hex2EncodedInt1:
+ readNumericVectorHelper<signed char>(v, rawData);
+ break;
+ case Hex2EncodedInt2:
+ readNumericVectorHelper<short>(v, rawData);
+ break;
+ case Hex2EncodedInt4:
+ readNumericVectorHelper<int>(v, rawData);
+ break;
+ case Hex2EncodedInt8:
+ readNumericVectorHelper<qint64>(v, rawData);
+ break;
+ case Hex2EncodedUInt1:
+ readNumericVectorHelper<uchar>(v, rawData);
+ break;
+ case Hex2EncodedUInt2:
+ readNumericVectorHelper<ushort>(v, rawData);
+ break;
+ case Hex2EncodedUInt4:
+ readNumericVectorHelper<uint>(v, rawData);
+ break;
+ case Hex2EncodedUInt8:
+ readNumericVectorHelper<quint64>(v, rawData);
+ break;
+ case Hex2EncodedFloat4:
+ readNumericVectorHelper<float>(v, rawData);
+ break;
+ case Hex2EncodedFloat8:
+ readNumericVectorHelper<double>(v, rawData);
+ break;
+ default:
+ qDebug() << "ENCODING ERROR: " << encoding;
+ }
+
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index 5aa68af965..58a61609fc 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -37,6 +37,7 @@
#include <QMetaType>
#include <functional>
+#include <vector>
namespace Debugger {
namespace Internal {
@@ -113,6 +114,7 @@ public:
QString value; // Displayed value
QByteArray editvalue; // Displayed value
DebuggerDisplay editformat; // Format of displayed value
+ DebuggerEncoding editencoding; // Encoding of displayed value
QByteArray type; // Type for further processing
QString displayedType; // Displayed type (optional)
quint64 address; // Displayed address of the actual object
@@ -134,6 +136,10 @@ void decodeArrayData(std::function<void(const WatchData &)> itemHandler,
const QByteArray &rawData,
int encoding);
+void readNumericVector(std::vector<double> *,
+ const QByteArray &rawData,
+ DebuggerEncoding encoding);
+
void parseChildrenData(const WatchData &parent, const GdbMi &child,
std::function<void(const WatchData &)> itemHandler,
std::function<void(const WatchData &, const GdbMi &)> childHandler,
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 9ac7dbbeda..cc27ae30c1 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -53,11 +53,13 @@
#include <QDebug>
#include <QFile>
+#include <QPainter>
#include <QProcess>
#include <QTabWidget>
#include <QTextEdit>
#include <QTimer>
+#include <algorithm>
#include <cstring>
#include <ctype.h>
@@ -109,6 +111,52 @@ static void saveWatchers()
setSessionValue("Watchers", WatchHandler::watchedExpressions());
}
+static void loadFormats()
+{
+ QVariant value = sessionValue("DefaultFormats");
+ QMapIterator<QString, QVariant> it(value.toMap());
+ while (it.hasNext()) {
+ it.next();
+ if (!it.key().isEmpty())
+ theTypeFormats.insert(it.key().toUtf8(), it.value().toInt());
+ }
+
+ value = sessionValue("IndividualFormats");
+ it = QMapIterator<QString, QVariant>(value.toMap());
+ while (it.hasNext()) {
+ it.next();
+ if (!it.key().isEmpty())
+ theIndividualFormats.insert(it.key().toUtf8(), it.value().toInt());
+ }
+}
+
+static void saveFormats()
+{
+ QMap<QString, QVariant> formats;
+ QHashIterator<QByteArray, int> it(theTypeFormats);
+ while (it.hasNext()) {
+ it.next();
+ const int format = it.value();
+ if (format != AutomaticFormat) {
+ const QByteArray key = it.key().trimmed();
+ if (!key.isEmpty())
+ formats.insert(QString::fromLatin1(key), format);
+ }
+ }
+ setSessionValue("DefaultFormats", formats);
+
+ formats.clear();
+ it = QHashIterator<QByteArray, int>(theIndividualFormats);
+ while (it.hasNext()) {
+ it.next();
+ const int format = it.value();
+ const QByteArray key = it.key().trimmed();
+ if (!key.isEmpty())
+ formats.insert(QString::fromLatin1(key), format);
+ }
+ setSessionValue("IndividualFormats", formats);
+}
+
///////////////////////////////////////////////////////////////////////
//
// SeparatedView
@@ -126,17 +174,29 @@ public:
setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
QVariant geometry = sessionValue("DebuggerSeparateWidgetGeometry");
- if (geometry.isValid())
- setGeometry(geometry.toRect());
+ if (geometry.isValid()) {
+ QRect rc = geometry.toRect();
+ if (rc.width() < 200)
+ rc.setWidth(200);
+ if (rc.height() < 200)
+ rc.setHeight(200);
+ setGeometry(rc);
+ }
}
- ~SeparatedView()
+ void saveGeometry()
{
setSessionValue("DebuggerSeparateWidgetGeometry", geometry());
}
+ ~SeparatedView()
+ {
+ saveGeometry();
+ }
+
void removeObject(const QByteArray &key)
{
+ saveGeometry();
if (QWidget *w = findWidget(key)) {
removeTab(indexOf(w));
sanitize();
@@ -145,9 +205,11 @@ public:
void closeTab(int index)
{
+ saveGeometry();
if (QObject *o = widget(index)) {
QByteArray iname = o->property(INameProperty).toByteArray();
theIndividualFormats.remove(iname);
+ saveFormats();
}
removeTab(index);
sanitize();
@@ -170,7 +232,7 @@ public:
return 0;
}
- template <class T> T *prepareObject(const QByteArray &key, const QString &title)
+ template <class T> T *prepareObject(const QByteArray &key, const QString &tabName)
{
T *t = 0;
if (QWidget *w = findWidget(key)) {
@@ -181,7 +243,7 @@ public:
if (!t) {
t = new T;
t->setProperty(KeyProperty, key);
- addTab(t, title);
+ addTab(t, tabName);
}
setCurrentWidget(t);
@@ -218,8 +280,9 @@ public:
void insertItem(WatchItem *item);
void reexpandItems();
- void showEditValue(const WatchData &data);
- void setFormat(const QByteArray &type, int format);
+ void showEditValue(const WatchItem *item);
+ void setTypeFormat(const QByteArray &type, int format);
+ void setIndividualFormat(const QByteArray &iname, int format);
QString removeNamespaces(QString str) const;
@@ -884,16 +947,12 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
break;
case LocalsTypeFormatRole:
- setFormat(item->type, value.toInt());
+ setTypeFormat(item->type, value.toInt());
m_engine->updateWatchItem(item);
break;
case LocalsIndividualFormatRole: {
- const int format = value.toInt();
- if (format == AutomaticFormat)
- theIndividualFormats.remove(item->iname);
- else
- theIndividualFormats[item->iname] = format;
+ setIndividualFormat(item->iname, value.toInt());
m_engine->updateWatchItem(item);
break;
}
@@ -1010,7 +1069,7 @@ DisplayFormats WatchItem::typeFormatList() const
formats << watchModel()->m_reportedTypeFormats.value(t);
if (t.contains(QLatin1Char(']')))
- formats << watchModel()->m_reportedTypeFormats.value(QLatin1String("[]"));
+ formats.append(ArrayPlotFormat);
// Fixed artificial string and pointer types.
if (origaddr || isPointerType(type)) {
@@ -1132,7 +1191,7 @@ void WatchModel::reinsertAllData()
emit inameIsExpanded(parent->iname);
emit itemIsExpanded(indexFromItem(parent));
}
- showEditValue(data);
+ showEditValue(newItem); // FIXME: Needed?
}
}
@@ -1192,7 +1251,7 @@ void WatchModel::insertItem(WatchItem *item)
const int row = findInsertPosition(parent->children(), item);
parent->insertChild(row, item);
- item->walkTree([this](TreeItem *sub) { showEditValue(*static_cast<WatchItem *>(sub)); });
+ item->walkTree([this](TreeItem *sub) { showEditValue(static_cast<WatchItem *>(sub)); });
}
void WatchModel::reexpandItems()
@@ -1326,20 +1385,20 @@ static void swapEndian(char *d, int nchar)
}
}
-void WatchModel::showEditValue(const WatchData &data)
+void WatchModel::showEditValue(const WatchItem *item)
{
- const QByteArray key = data.address ? data.hexAddress() : data.iname;
- switch (data.editformat) {
+ const QByteArray key = item->address ? item->hexAddress() : item->iname;
+ switch (item->editformat) {
case StopDisplay:
- m_separatedView->removeObject(data.iname);
+ m_separatedView->removeObject(key);
break;
case DisplayImageData:
case DisplayImageFile: { // QImage
int width = 0, height = 0, nbytes = 0, format = 0;
QByteArray ba;
uchar *bits = 0;
- if (data.editformat == DisplayImageData) {
- ba = QByteArray::fromHex(data.editvalue);
+ if (item->editformat == DisplayImageData) {
+ ba = QByteArray::fromHex(item->editvalue);
QTC_ASSERT(ba.size() > 16, return);
const int *header = (int *)(ba.data());
if (!ba.at(0) && !ba.at(1)) // Check on 'width' for Python dumpers returning 4-byte swapped-data.
@@ -1349,8 +1408,8 @@ void WatchModel::showEditValue(const WatchData &data)
height = header[1];
nbytes = header[2];
format = header[3];
- } else if (data.editformat == DisplayImageFile) {
- QTextStream ts(data.editvalue);
+ } else if (item->editformat == DisplayImageFile) {
+ QTextStream ts(item->editvalue);
QString fileName;
ts >> width >> height >> nbytes >> format >> fileName;
QFile f(fileName);
@@ -1365,33 +1424,45 @@ void WatchModel::showEditValue(const WatchData &data)
QTC_ASSERT(0 < format && format < 32, return);
QImage im(width, height, QImage::Format(format));
std::memcpy(im.bits(), bits, nbytes);
- const QString title = data.address ?
- tr("%1 Object at %2").arg(QLatin1String(data.type),
- QLatin1String(data.hexAddress())) :
- tr("%1 Object at Unknown Address").arg(QLatin1String(data.type));
- ImageViewer *v = m_separatedView->prepareObject<ImageViewer>(key, title);
- v->setProperty(INameProperty, data.iname);
+ ImageViewer *v = m_separatedView->prepareObject<ImageViewer>(key, item->name);
+ v->setProperty(INameProperty, item->iname);
+ v->setInfo(item->address ?
+ tr("%1 Object at %2").arg(QLatin1String(item->type),
+ QLatin1String(item->hexAddress())) :
+ tr("%1 Object at Unknown Address").arg(QLatin1String(item->type))
+ + QLatin1String(" ") +
+ ImageViewer::tr("Size: %1x%2, %3 byte, format: %4, depth: %5")
+ .arg(width).arg(height).arg(nbytes).arg(im.format()).arg(im.depth())
+ );
v->setImage(im);
break;
}
case DisplayUtf16String:
case DisplayLatin1String:
case DisplayUtf8String: { // String data.
- QByteArray ba = QByteArray::fromHex(data.editvalue);
+ QByteArray ba = QByteArray::fromHex(item->editvalue);
QString str;
- if (data.editformat == DisplayUtf16String)
+ if (item->editformat == DisplayUtf16String)
str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
- else if (data.editformat == DisplayLatin1String)
+ else if (item->editformat == DisplayLatin1String)
str = QString::fromLatin1(ba.constData(), ba.size());
- else if (data.editformat == DisplayUtf8String)
+ else if (item->editformat == DisplayUtf8String)
str = QString::fromUtf8(ba.constData(), ba.size());
- QTextEdit *t = m_separatedView->prepareObject<QTextEdit>(key, data.name);
- t->setProperty(INameProperty, data.iname);
+ QTextEdit *t = m_separatedView->prepareObject<QTextEdit>(key, item->name);
+ t->setProperty(INameProperty, item->iname);
t->setText(str);
break;
}
+ case DisplayPlotData: { // Plots
+ std::vector<double> data;
+ readNumericVector(&data, QByteArray::fromHex(item->editvalue), item->editencoding);
+ PlotViewer *v = m_separatedView->prepareObject<PlotViewer>(key, item->name);
+ v->setProperty(INameProperty, item->iname);
+ v->setData(data);
+ break;
+ }
default:
- QTC_ASSERT(false, qDebug() << "Display format: " << data.editformat);
+ QTC_ASSERT(false, qDebug() << "Display format: " << item->editformat);
break;
}
}
@@ -1439,52 +1510,6 @@ QStringList WatchHandler::watchedExpressions()
return watcherNames;
}
-static void loadFormats()
-{
- QVariant value = sessionValue("DefaultFormats");
- QMapIterator<QString, QVariant> it(value.toMap());
- while (it.hasNext()) {
- it.next();
- if (!it.key().isEmpty())
- theTypeFormats.insert(it.key().toUtf8(), it.value().toInt());
- }
-
- value = sessionValue("IndividualFormats");
- it = QMapIterator<QString, QVariant>(value.toMap());
- while (it.hasNext()) {
- it.next();
- if (!it.key().isEmpty())
- theIndividualFormats.insert(it.key().toUtf8(), it.value().toInt());
- }
-}
-
-static void saveFormats()
-{
- QMap<QString, QVariant> formats;
- QHashIterator<QByteArray, int> it(theTypeFormats);
- while (it.hasNext()) {
- it.next();
- const int format = it.value();
- if (format != AutomaticFormat) {
- const QByteArray key = it.key().trimmed();
- if (!key.isEmpty())
- formats.insert(QString::fromLatin1(key), format);
- }
- }
- setSessionValue("DefaultFormats", formats);
-
- formats.clear();
- it = QHashIterator<QByteArray, int>(theIndividualFormats);
- while (it.hasNext()) {
- it.next();
- const int format = it.value();
- const QByteArray key = it.key().trimmed();
- if (!key.isEmpty())
- formats.insert(QString::fromLatin1(key), format);
- }
- setSessionValue("IndividualFormats", formats);
-}
-
void WatchHandler::saveSessionData()
{
saveWatchers();
@@ -1537,7 +1562,7 @@ const WatchItem *WatchHandler::findCppLocalVariable(const QString &name) const
return 0;
}
-void WatchModel::setFormat(const QByteArray &type0, int format)
+void WatchModel::setTypeFormat(const QByteArray &type0, int format)
{
const QByteArray type = stripForFormat(type0);
if (format == AutomaticFormat)
@@ -1548,6 +1573,15 @@ void WatchModel::setFormat(const QByteArray &type0, int format)
reinsertAllData();
}
+void WatchModel::setIndividualFormat(const QByteArray &iname, int format)
+{
+ if (format == AutomaticFormat)
+ theIndividualFormats.remove(iname);
+ else
+ theIndividualFormats[iname] = format;
+ saveFormats();
+}
+
int WatchHandler::format(const QByteArray &iname) const
{
int result = AutomaticFormat;