diff options
author | hjk <hjk@theqtcompany.com> | 2015-04-01 17:19:43 +0200 |
---|---|---|
committer | hjk <hjk@theqtcompany.com> | 2015-04-02 12:25:04 +0000 |
commit | 3658bdac898c94a10eef2f04546c459eab424a5c (patch) | |
tree | a1c35d92930ada15a26f67a74a98d64cbd15d387 | |
parent | 3e82dcad4435a9c0747f6880aaa7e0a968156780 (diff) | |
download | qt-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.py | 249 | ||||
-rw-r--r-- | share/qtcreator/debugger/gdbbridge.py | 2 | ||||
-rw-r--r-- | share/qtcreator/debugger/qttypes.py | 17 | ||||
-rw-r--r-- | share/qtcreator/debugger/stdtypes.py | 8 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerprotocol.h | 3 | ||||
-rw-r--r-- | src/plugins/debugger/imageviewer.cpp | 85 | ||||
-rw-r--r-- | src/plugins/debugger/imageviewer.h | 25 | ||||
-rw-r--r-- | src/plugins/debugger/watchdata.cpp | 57 | ||||
-rw-r--r-- | src/plugins/debugger/watchdata.h | 6 | ||||
-rw-r--r-- | src/plugins/debugger/watchhandler.cpp | 202 |
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; |