Checkpoint: Improving memory visualizer.

This commit is contained in:
Ernie Pasveer
2022-05-22 12:47:52 -05:00
parent ea9b3ae57b
commit 171ca4efe6
3 changed files with 526 additions and 27 deletions
+245 -10
View File
@@ -4,30 +4,89 @@
#include <QtGui/QPaintEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QClipboard>
#include <QtGui/QTextCursor>
#include <QtCore/QSize>
#include <QtCore/QDebug>
#include <stdexcept>
SeerHexWidget::SeerHexWidget(QWidget* parent) : QPlainTextEdit(parent), _pdata(NULL) {
#define byteArrayToType( data, order, precision, type ) \
QDataStream stream( data ); \
stream.setByteOrder( order ); \
stream.setFloatingPointPrecision( precision ); \
type t; \
stream >> t; \
return t;
qint8 toQInt8 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, qint8)
}
quint8 toQUInt8 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, quint8)
}
qint16 toQInt16 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, qint16)
}
quint16 toQUInt16 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, quint16)
}
qint32 toQInt32 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, qint32)
}
quint32 toQUInt32 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, quint32)
}
qint64 toQInt64 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, qint64)
}
quint64 toQUInt64 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, quint64)
}
float toQFloat32 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::SinglePrecision, float)
}
double toQFloat64 (const QByteArray& data, const QDataStream::ByteOrder order=QDataStream::BigEndian) {
byteArrayToType(data, order, QDataStream::DoublePrecision, double)
}
SeerHexWidget::SeerHexWidget(QWidget* parent) : QWidget(parent), _pdata(NULL) {
// Construct the UI.
setupUi(this);
// Setup the widgets
QFont font;
font.setFamily("monospace [Consolas]");
font.setFixedPitch(true);
font.setStyleHint(QFont::TypeWriter);
setFont(font);
plainTextEdit->setFont(font);
plainTextEdit->setFocusPolicy(Qt::StrongFocus);
plainTextEdit->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
_memoryMode = SeerHexWidget::HexMemoryMode;
_charMode = SeerHexWidget::AsciiCharMode;
_addressOffset = 0;
_charWidth = fontMetrics().horizontalAdvance(QLatin1Char('9'));
_charHeight = fontMetrics().height();
_charWidth = plainTextEdit->fontMetrics().horizontalAdvance(QLatin1Char('9'));
_charHeight = plainTextEdit->fontMetrics().height();
_gapAddrHex = 10; // Gap between address and hex fields.
_gapHexAscii = 16; // Gap between hex and ascii fields.
setBytesPerLine(16);
setFocusPolicy(Qt::StrongFocus);
// Connect things.
QObject::connect(plainTextEdit, &QPlainTextEdit::cursorPositionChanged, this, &SeerHexWidget::handleCursorPositionChanged);
QObject::connect(showAsLittleEndianCheckBox, &QCheckBox::clicked, this, &SeerHexWidget::handleCursorPositionChanged);
QObject::connect(showUnsignedFloatAsHexCheckBox, &QCheckBox::clicked, this, &SeerHexWidget::handleCursorPositionChanged);
QObject::connect(this, &SeerHexWidget::byteOffsetChanged, this, &SeerHexWidget::handleByteOffsetChanged);
}
SeerHexWidget::~SeerHexWidget() {
@@ -58,7 +117,7 @@ void SeerHexWidget::setBytesPerLine (int count) {
_bytesPerLine = count;
_hexCharsPerLine = _bytesPerLine * _hexCharsPerByte - 1;
_posAddr = 0; // x position of address field.
_posHex = 12 * _charWidth + gapAddrHex(); // x position of hex field.
_posHex = SeerHexWidget::HexFieldWidth * _charWidth + gapAddrHex(); // x position of hex field.
_posAscii = _posHex + hexCharsPerLine() * _charWidth + gapHexAscii(); // x position of ascii field.
setMinimumWidth(_posAscii + (bytesPerLine() * _charWidth)); // x position after the ascii field.
@@ -75,6 +134,14 @@ int SeerHexWidget::hexCharsPerLine () const {
return _hexCharsPerLine;
}
int SeerHexWidget::hexCharsPerByte () const {
return _hexCharsPerByte;
}
int SeerHexWidget::nLines () const {
return plainTextEdit->blockCount();
}
int SeerHexWidget::gapAddrHex () const {
return _gapAddrHex;
}
@@ -154,6 +221,16 @@ QString SeerHexWidget::charModeString () const {
return "???";
}
QTextDocument* SeerHexWidget::document () {
return plainTextEdit->document();
}
QString SeerHexWidget::toPlainText () {
return plainTextEdit->toPlainText();
}
void SeerHexWidget::setData(SeerHexWidget::DataStorage* pData) {
if (_pdata) {
@@ -167,10 +244,168 @@ void SeerHexWidget::setData(SeerHexWidget::DataStorage* pData) {
create();
}
void SeerHexWidget::handleCursorPositionChanged () {
// Get the current cursor position.
QTextCursor cursor = plainTextEdit->textCursor();
// Is it before the hex values? (address region)
if (cursor.positionInBlock() < SeerHexWidget::HexFieldWidth) {
emit byteOffsetChanged(-1);
return;
}
// Is is after the hex values? (ascii/ebcdic region)
if (cursor.positionInBlock() > SeerHexWidget::HexFieldWidth + hexCharsPerLine()) {
emit byteOffsetChanged(-1);
return;
}
//qDebug() << '(' << cursor.blockNumber() << ',' << cursor.positionInBlock() << ')';
int line = cursor.blockNumber();
int col = cursor.positionInBlock() - SeerHexWidget::HexFieldWidth;
int byte = (col / hexCharsPerByte()) + (line * bytesPerLine());
//qDebug() << "(" << line << "," << col << ") =" << byte;
emit byteOffsetChanged(byte);
}
void SeerHexWidget::handleByteOffsetChanged (int byte) {
//qDebug() << byte;
// Clear all fields.
lineEdit_1->setText("");
lineEdit_2->setText("");
lineEdit_3->setText("");
lineEdit_4->setText("");
lineEdit_5->setText("");
lineEdit_6->setText("");
lineEdit_7->setText("");
lineEdit_8->setText("");
lineEdit_9->setText("");
lineEdit_10->setText("");
if (byte < 0) {
return;
}
// If there's no data, do nothing.
if (!_pdata) {
return;
}
// Set the endian default.
QDataStream::ByteOrder byteOrder = QDataStream::BigEndian;
if (showAsLittleEndianCheckBox->isChecked()) {
byteOrder = QDataStream::LittleEndian;
}
// Set the 'AsHex' default.
bool unsignedAndFloatAsHex = false;
if (showUnsignedFloatAsHexCheckBox->isChecked()) {
unsignedAndFloatAsHex = true;
}
// Go through each one and display the value.
{
QByteArray arr = _pdata->getData(byte, sizeof(char)); // Extract a bytearray from the data for the size of the value we are after.
if (arr.size() == sizeof(char)) { // If not the right size, skip it. Near the end of the data.
lineEdit_1->setText(QString::number(toQInt8(arr, byteOrder))); // Fill in the signed value.
if (unsignedAndFloatAsHex) { // Show unsigned value as hex?
if (byteOrder == QDataStream::LittleEndian) { // Swap bytes to handle endianess.
std::reverse(arr.begin(), arr.end());
}
lineEdit_2->setText("0x"+QString(arr.toHex())); // Print value as hex.
}else{
lineEdit_2->setText(QString::number(toQUInt8(arr, byteOrder))); // Print value as a value.
}
}
}
{
QByteArray arr = _pdata->getData(byte, sizeof(short));
if (arr.size() == sizeof(short)) {
lineEdit_3->setText(QString::number(toQInt16(arr, byteOrder)));
if (unsignedAndFloatAsHex) {
if (byteOrder == QDataStream::LittleEndian) {
std::reverse(arr.begin(), arr.end());
}
lineEdit_4->setText("0x"+QString(arr.toHex()));
}else{
lineEdit_4->setText(QString::number(toQUInt16(arr, byteOrder)));
}
}
}
{
QByteArray arr = _pdata->getData(byte, sizeof(float));
if (arr.size() == sizeof(float)) {
if (unsignedAndFloatAsHex) {
if (byteOrder == QDataStream::LittleEndian) {
std::reverse(arr.begin(), arr.end());
}
lineEdit_5->setText("0x"+QString(arr.toHex()));
}else{
lineEdit_5->setText(QString::number(toQFloat32(arr, byteOrder)));
}
}
}
{
QByteArray arr = _pdata->getData(byte, sizeof(int));
if (arr.size() == sizeof(int)) {
lineEdit_6->setText(QString::number(toQInt32(arr, byteOrder)));
if (unsignedAndFloatAsHex) {
if (byteOrder == QDataStream::LittleEndian) {
std::reverse(arr.begin(), arr.end());
}
lineEdit_7->setText("0x"+QString(arr.toHex()));
}else{
lineEdit_7->setText(QString::number(toQUInt32(arr, byteOrder)));
}
}
}
{
QByteArray arr = _pdata->getData(byte, sizeof(long int));
if (arr.size() == sizeof(long int)) {
lineEdit_8->setText(QString::number(toQInt64(arr, byteOrder)));
if (unsignedAndFloatAsHex) {
if (byteOrder == QDataStream::LittleEndian) {
std::reverse(arr.begin(), arr.end());
}
lineEdit_9->setText("0x"+QString(arr.toHex()));
}else{
lineEdit_9->setText(QString::number(toQUInt64(arr, byteOrder)));
}
}
}
{
QByteArray arr = _pdata->getData(byte, sizeof(double));
if (arr.size() == sizeof(double)) {
if (unsignedAndFloatAsHex) {
if (byteOrder == QDataStream::LittleEndian) {
std::reverse(arr.begin(), arr.end());
}
lineEdit_10->setText("0x"+QString(arr.toHex()));
}else{
lineEdit_10->setText(QString::number(toQFloat64(arr, byteOrder)));
}
}
}
}
void SeerHexWidget::create () {
// Clear the current document. We're going to recreate it.
clear();
plainTextEdit->clear();
// If there's no data, do nothing.
if (!_pdata) {
@@ -178,12 +413,12 @@ void SeerHexWidget::create () {
}
// Set text formats.
QTextCharFormat defaultFormat = currentCharFormat();
QTextCharFormat defaultFormat = plainTextEdit->currentCharFormat();
QTextCharFormat grayFormat = defaultFormat;
grayFormat.setBackground(QBrush(Qt::lightGray));
// Get a cursor
QTextCursor cursor(textCursor());
QTextCursor cursor(plainTextEdit->textCursor());
cursor.movePosition(QTextCursor::Start);
@@ -195,7 +430,7 @@ void SeerHexWidget::create () {
// Place a new hex address on the left side.
if (i % bytesPerLine() == 0) {
QString address = QString("%1").arg(i + addressOffset(), 12, 16, QChar('0'));
QString address = QString("%1").arg(i + addressOffset(), SeerHexWidget::HexFieldWidth, 16, QChar('0'));
QString spacer(" ");
// Write adress to document.
+36 -17
View File
@@ -2,6 +2,7 @@
#include <QtWidgets/QPlainTextEdit>
#include <QtCore/QByteArray>
#include "ui_SeerHexWidget.h"
//
// Hex Memory viewer widget.
@@ -11,7 +12,9 @@
// MIT License.
//
class SeerHexWidget: public QPlainTextEdit {
class SeerHexWidget: public QWidget, protected Ui::SeerHexWidgetForm {
Q_OBJECT
public:
class DataStorage {
@@ -44,33 +47,49 @@ class SeerHexWidget: public QPlainTextEdit {
EbcdicCharMode = 2
};
enum MagicNumbers {
HexFieldWidth = 12
};
SeerHexWidget(QWidget* parent = 0);
~SeerHexWidget();
void setBytesPerLine (int count);
int bytesPerLine () const;
int hexCharsPerLine () const;
int gapAddrHex () const;
int gapHexAscii () const;
void setAddressOffset (unsigned long offset);
unsigned long addressOffset () const;
unsigned long size () const;
void setBytesPerLine (int count);
int bytesPerLine () const;
int hexCharsPerLine () const;
int hexCharsPerByte () const;
int nLines () const;
int gapAddrHex () const;
int gapHexAscii () const;
void setAddressOffset (unsigned long offset);
unsigned long addressOffset () const;
unsigned long size () const;
void setMemoryMode (SeerHexWidget::MemoryMode memoryMode);
SeerHexWidget::MemoryMode memoryMode () const;
QString memoryModeString () const;
void setMemoryMode (SeerHexWidget::MemoryMode memoryMode);
SeerHexWidget::MemoryMode memoryMode () const;
QString memoryModeString () const;
void setCharMode (SeerHexWidget::CharMode charMode);
SeerHexWidget::CharMode charMode () const;
QString charModeString () const;
void setCharMode (SeerHexWidget::CharMode charMode);
SeerHexWidget::CharMode charMode () const;
QString charModeString () const;
QTextDocument* document ();
QString toPlainText ();
signals:
void byteOffsetChanged (int byte);
public slots:
void setData (DataStorage* pData);
void setData (DataStorage* pData);
protected:
protected slots:
void handleCursorPositionChanged ();
void handleByteOffsetChanged (int byte);
private:
void create ();
void create ();
DataStorage* _pdata;
int _posAddr;
+245
View File
@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SeerHexWidgetForm</class>
<widget class="QWidget" name="SeerHexWidgetForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>907</width>
<height>643</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>Signed 8 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_1"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Signed 32 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="lineEdit_6"/>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Hexadecimal</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QLineEdit" name="lineEdit_11"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Unsigned 8 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_2"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Unsigned 32 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="lineEdit_7"/>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Octal</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QLineEdit" name="lineEdit_12"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Signed 16 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_3"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Signed 64 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="lineEdit_8"/>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Binary</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QLineEdit" name="lineEdit_13"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Unsigned 16 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineEdit_4"/>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Unsigned 64 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLineEdit" name="lineEdit_9"/>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Stream Length</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QLineEdit" name="lineEdit_14"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Float 32 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineEdit_5"/>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Float 64 bit</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLineEdit" name="lineEdit_10"/>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="showAsLittleEndianCheckBox">
<property name="text">
<string>Show Little Endian Decoding</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showUnsignedFloatAsHexCheckBox">
<property name="text">
<string>Show Unsigned and Float as Hexidecimal</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>