Files
seer/src/SeerArrayVisualizerWidget.cpp
T

422 lines
14 KiB
C++
Raw Normal View History

2021-11-30 16:59:42 -06:00
#include "SeerArrayVisualizerWidget.h"
#include "SeerUtl.h"
2022-03-24 15:04:18 -05:00
#include <QtCharts/QLineSeries>
#include <QtCharts/QSplineSeries>
#include <QtCharts/QScatterSeries>
2021-11-30 16:59:42 -06:00
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QFileDialog>
2022-03-21 17:37:53 -05:00
#include <QtWidgets/QToolTip>
2021-11-30 16:59:42 -06:00
#include <QtGui/QIntValidator>
#include <QtGui/QIcon>
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>
#include <QtCore/QRegExp>
#include <QtCore/QSettings>
#include <QtCore/QDebug>
SeerArrayVisualizerWidget::SeerArrayVisualizerWidget (QWidget* parent) : QWidget(parent) {
// Init variables.
_variableId = Seer::createID(); // Create two id's for queries.
_memoryId = Seer::createID();
2022-03-19 15:49:40 -05:00
_series = 0;
2021-11-30 16:59:42 -06:00
// Set up UI.
setupUi(this);
// Setup the widgets
setWindowIcon(QIcon(":/seer/resources/seer_64x64.png"));
setWindowTitle("Seer Array Visualizer");
arrayLengthLineEdit->setValidator(new QIntValidator(1, 9999999, this));
2022-03-21 17:37:53 -05:00
arrayOffsetLineEdit->setValidator(new QIntValidator(0, 9999999, this));
arrayStrideLineEdit->setValidator(new QIntValidator(1, 9999999, this));
2021-11-30 16:59:42 -06:00
2022-03-24 15:04:18 -05:00
lineRadioButton->setChecked(true);
2021-11-30 16:59:42 -06:00
arrayDisplayFormatComboBox->setCurrentIndex(0);
2022-03-14 18:28:46 -05:00
handleArrayDisplayFormatComboBox(0);
2021-11-30 16:59:42 -06:00
2022-03-21 17:37:53 -05:00
// A single series chart.
2022-03-19 15:49:40 -05:00
QChart* chart = new QChart;
chart->legend()->hide();
chart->createDefaultAxes();
chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignBottom);
arrayChartView->setRenderHint(QPainter::Antialiasing);
arrayChartView->setChart(chart);
2021-11-30 16:59:42 -06:00
// Connect things.
QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerArrayVisualizerWidget::handleRefreshButton);
QObject::connect(arrayLengthLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleRefreshButton);
2022-03-24 15:04:18 -05:00
QObject::connect(arrayOffsetLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleRefreshButton);
QObject::connect(arrayStrideLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleRefreshButton);
2021-11-30 16:59:42 -06:00
QObject::connect(variableNameLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleVariableNameLineEdit);
QObject::connect(arrayDisplayFormatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SeerArrayVisualizerWidget::handleArrayDisplayFormatComboBox);
2022-03-19 15:49:40 -05:00
QObject::connect(arrayTableWidget, &SeerArrayWidget::dataChanged, this, &SeerArrayVisualizerWidget::handleDataChanged);
2022-03-21 17:37:53 -05:00
QObject::connect(splitter, &QSplitter::splitterMoved, this, &SeerArrayVisualizerWidget::handleSplitterMoved);
QObject::connect(titleLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleTitleLineEdit);
QObject::connect(pointsCheckBox, &QCheckBox::clicked, this, &SeerArrayVisualizerWidget::handlePointsCheckBox);
QObject::connect(labelsCheckBox, &QCheckBox::clicked, this, &SeerArrayVisualizerWidget::handleLabelsCheckBox);
2022-04-28 20:20:37 -05:00
QObject::connect(lineTypeButtonGroup, QOverload<int>::of(&QButtonGroup::idClicked), this, &SeerArrayVisualizerWidget::handleLineTypeButtonGroup);
2021-11-30 16:59:42 -06:00
// Restore window settings.
readSettings();
}
SeerArrayVisualizerWidget::~SeerArrayVisualizerWidget () {
}
void SeerArrayVisualizerWidget::setVariableName (const QString& name) {
setWindowTitle("Seer Array Visualizer - '" + name + "'");
variableNameLineEdit->setText(name);
setVariableAddress("");
if (variableNameLineEdit->text() == "") {
return;
}
// Clear old contents.
QByteArray array;
2022-03-24 15:04:18 -05:00
bool ok;
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
arrayTableWidget->setData(new SeerArrayWidget::DataStorageArray(array));
2021-11-30 16:59:42 -06:00
2022-03-24 15:04:18 -05:00
if (arrayOffsetLineEdit->text() != "") {
arrayTableWidget->setAddressOffset(arrayOffsetLineEdit->text().toULong(&ok));
if (ok == false) {
qWarning() << "Invalid string for address offset." << arrayOffsetLineEdit->text();
}
}else{
arrayTableWidget->setAddressOffset(0);
}
if (arrayStrideLineEdit->text() != "") {
arrayTableWidget->setAddressStride(arrayStrideLineEdit->text().toULong(&ok));
if (ok == false) {
qWarning() << "Invalid string for address stride." << arrayStrideLineEdit->text();
}
}else{
arrayTableWidget->setAddressStride(1);
}
2021-11-30 16:59:42 -06:00
// Send signal to get variable address.
emit evaluateVariableExpression(_variableId, variableNameLineEdit->text());
}
QString SeerArrayVisualizerWidget::variableName () const {
return variableNameLineEdit->text();
}
void SeerArrayVisualizerWidget::setVariableAddress (const QString& address) {
if (address.startsWith("0x")) {
2022-04-02 13:41:17 -05:00
bool ok = false;
address.toULong(&ok, 16);
2021-11-30 16:59:42 -06:00
if (ok == true) {
variableAddressLineEdit->setText(address);
}else{
variableAddressLineEdit->setText("not an address");
}
}else{
variableAddressLineEdit->setText("not an address");
}
2022-03-24 15:04:18 -05:00
arrayTableWidget->setAddressOffset(0);
2021-11-30 16:59:42 -06:00
}
QString SeerArrayVisualizerWidget::variableAddress () const {
return variableAddressLineEdit->text();
}
void SeerArrayVisualizerWidget::handleText (const QString& text) {
//qDebug() << text;
2021-11-30 16:59:42 -06:00
if (text.contains(QRegExp("^([0-9]+)\\^done,value="))) {
// 10^done,value="1"
// 11^done,value="0x7fffffffd538"
QString id_text = text.section('^', 0,0);
if (id_text.toInt() == _variableId) {
2022-04-28 20:20:37 -05:00
QStringList words = Seer::filterEscapes(Seer::parseFirst(text, "value=", '"', '"', false)).split(' ', Qt::SkipEmptyParts);
2021-11-30 16:59:42 -06:00
setVariableAddress(words.first());
}
}else if (text.contains(QRegExp("^([0-9]+)\\^done,memory="))) {
// 3^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613e71",contents="00"}]
// 4^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613ed4",contents="000000000000000000000000"}]
QString id_text = text.section('^', 0,0);
if (id_text.toInt() == _memoryId) {
2022-03-19 15:49:40 -05:00
//qDebug() << text;
2021-11-30 16:59:42 -06:00
QString memory_text = Seer::parseFirst(text, "memory=", '[', ']', false);
QStringList range_list = Seer::parse(memory_text, "", '{', '}', false);
// Loop through the memory ranges.
for ( const auto& range_text : range_list ) {
QString contents_text = Seer::parseFirst(range_text, "contents=", '"', '"', false);
//qDebug() << contents_text;
2021-11-30 16:59:42 -06:00
// Convert hex string to byte array.
QByteArray array;
for (int i = 0; i<contents_text.size(); i += 2) {
QString num = contents_text.mid(i, 2);
bool ok = false;
array.push_back(num.toInt(&ok, 16));
2022-02-08 19:24:41 -06:00
Q_ASSERT(ok);
2021-11-30 16:59:42 -06:00
}
// Give the byte array to the hex widget.
2022-03-24 15:04:18 -05:00
bool ok;
2022-03-14 18:28:46 -05:00
arrayTableWidget->setData(new SeerArrayWidget::DataStorageArray(array));
2021-11-30 16:59:42 -06:00
2022-03-24 15:04:18 -05:00
if (arrayOffsetLineEdit->text() != "") {
arrayTableWidget->setAddressOffset(arrayOffsetLineEdit->text().toULong(&ok));
if (ok == false) {
qWarning() << "Invalid string for address offset." << arrayOffsetLineEdit->text();
}
}else{
arrayTableWidget->setAddressOffset(0);
}
if (arrayStrideLineEdit->text() != "") {
arrayTableWidget->setAddressStride(arrayStrideLineEdit->text().toULong(&ok));
if (ok == false) {
qWarning() << "Invalid string for address stride." << arrayStrideLineEdit->text();
}
}else{
arrayTableWidget->setAddressStride(1);
}
2021-11-30 16:59:42 -06:00
break; // Take just the first range for now.
}
}
}else if (text.contains(QRegExp("^([0-9]+)\\^error,msg="))) {
// 12^error,msg="No symbol \"return\" in current context."
// 13^error,msg="No symbol \"cout\" in current context."
// 3^error,msg="Unable to read memory."
QString id_text = text.section('^', 0,0);
if (id_text.toInt() == _variableId) {
variableAddressLineEdit->setText( Seer::filterEscapes(Seer::parseFirst(text, "msg=", '"', '"', false)) );
}
if (id_text.toInt() == _memoryId) {
// Display the error message.
QString msg_text = Seer::parseFirst(text, "msg=", false);
if (msg_text != "") {
QMessageBox::warning(this, "Error.", Seer::filterEscapes(msg_text));
}
}
}else{
// Ignore anything else.
}
}
void SeerArrayVisualizerWidget::handleRefreshButton () {
if (variableNameLineEdit->text() == "") {
return;
}
if (variableAddressLineEdit->text() == "") {
return;
}
if (variableAddressLineEdit->text() == "not an address") {
return;
}
2022-03-14 18:28:46 -05:00
int bytes = arrayLengthLineEdit->text().toInt() * Seer::typeBytes(arrayDisplayFormatComboBox->currentText());
2022-03-19 15:49:40 -05:00
//qDebug() << _memoryId << variableAddressLineEdit->text() << arrayLengthLineEdit->text() << arrayDisplayFormatComboBox->currentText() << bytes;
2022-03-14 18:28:46 -05:00
emit evaluateMemoryExpression(_memoryId, variableAddressLineEdit->text(), bytes);
2021-11-30 16:59:42 -06:00
}
void SeerArrayVisualizerWidget::handleVariableNameLineEdit () {
setVariableName (variableNameLineEdit->text());
}
void SeerArrayVisualizerWidget::handleArrayDisplayFormatComboBox (int index) {
2022-03-19 15:49:40 -05:00
//qDebug() << index;
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
if (index == 0) {
arrayTableWidget->setArrayMode(SeerArrayWidget::Int16ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 1) {
arrayTableWidget->setArrayMode(SeerArrayWidget::Int32ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 2) {
arrayTableWidget->setArrayMode(SeerArrayWidget::Int64ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 3) {
arrayTableWidget->setArrayMode(SeerArrayWidget::UInt16ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 4) {
arrayTableWidget->setArrayMode(SeerArrayWidget::UInt32ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 5) {
arrayTableWidget->setArrayMode(SeerArrayWidget::UInt64ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 6) {
arrayTableWidget->setArrayMode(SeerArrayWidget::Float32ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else if (index == 7) {
arrayTableWidget->setArrayMode(SeerArrayWidget::Float64ArrayMode);
2021-11-30 16:59:42 -06:00
2022-03-14 18:28:46 -05:00
}else{
// Do nothing.
2021-11-30 16:59:42 -06:00
}
2022-03-14 18:28:46 -05:00
}
2021-11-30 16:59:42 -06:00
2022-03-19 15:49:40 -05:00
void SeerArrayVisualizerWidget::handleDataChanged () {
if (_series) {
arrayChartView->chart()->removeSeries(_series);
delete _series;
_series = 0;
}
2022-03-24 15:04:18 -05:00
if (scatterRadioButton->isChecked()) {
QScatterSeries* scatter = new QScatterSeries;
scatter->setMarkerSize(7);
_series = scatter;
}else if (lineRadioButton->isChecked()) {
QLineSeries* line = new QLineSeries;
_series = line;
}else if (splineRadioButton->isChecked()) {
QSplineSeries* line = new QSplineSeries;
_series = line;
}
if (_series == 0) {
return;
}
2022-03-19 15:49:40 -05:00
_series->setName(variableName());
2022-03-21 17:37:53 -05:00
_series->setPointsVisible(false);
_series->setPointLabelsVisible(false);
2022-03-19 15:49:40 -05:00
const QVector<double>& values = arrayTableWidget->arrayValues();
for (int i = 0; i < values.size(); ++i) {
_series->append(i, values[i]);
}
2022-03-21 17:37:53 -05:00
QObject::connect(_series, &QLineSeries::hovered, this, &SeerArrayVisualizerWidget::handleSeriesHovered);
2022-03-19 15:49:40 -05:00
arrayChartView->chart()->addSeries(_series);
arrayChartView->chart()->createDefaultAxes();
}
2021-11-30 16:59:42 -06:00
void SeerArrayVisualizerWidget::writeSettings() {
QSettings settings;
2022-03-21 17:37:53 -05:00
settings.beginGroup("arrayvisualizerwindow"); {
settings.setValue("size", size());
settings.setValue("splitter", splitter->saveState());
} settings.endGroup();
2021-11-30 16:59:42 -06:00
//qDebug() << size();
2021-11-30 16:59:42 -06:00
}
void SeerArrayVisualizerWidget::readSettings() {
QSettings settings;
2022-03-21 17:37:53 -05:00
settings.beginGroup("arrayvisualizerwindow"); {
resize(settings.value("size", QSize(800, 400)).toSize());
splitter->restoreState(settings.value("splitter").toByteArray());
} settings.endGroup();
2021-11-30 16:59:42 -06:00
//qDebug() << size();
2021-11-30 16:59:42 -06:00
}
void SeerArrayVisualizerWidget::resizeEvent (QResizeEvent* event) {
writeSettings();
QWidget::resizeEvent(event);
}
2022-03-21 17:37:53 -05:00
void SeerArrayVisualizerWidget::handleSplitterMoved (int pos, int index) {
2022-04-28 20:20:37 -05:00
Q_UNUSED(pos);
Q_UNUSED(index);
2022-03-21 17:37:53 -05:00
writeSettings();
}
void SeerArrayVisualizerWidget::handleSeriesHovered (const QPointF& point, bool state) {
//qDebug() << "QPointF=" << point << "State=" << state << "MapToPosition=" << arrayChartView->chart()->mapToPosition(point) << "MapFromScene=" << arrayChartView->mapFromScene(arrayChartView->chart()->mapToPosition(point));
if (state) {
QToolTip::showText(arrayChartView->mapToGlobal(arrayChartView->mapFromScene(arrayChartView->chart()->mapToPosition(point))), QString("%1 / %2").arg(point.x()).arg(point.y()), this, QRect(), 10000);
}else{
QToolTip::hideText();
}
}
void SeerArrayVisualizerWidget::handleTitleLineEdit () {
arrayChartView->chart()->setTitle(titleLineEdit->text());
titleLineEdit->setText("");
}
void SeerArrayVisualizerWidget::handlePointsCheckBox () {
if (_series) {
_series->setPointsVisible(pointsCheckBox->isChecked());
}
}
void SeerArrayVisualizerWidget::handleLabelsCheckBox () {
if (_series) {
_series->setPointLabelsVisible(labelsCheckBox->isChecked());
}
}
2022-03-24 15:04:18 -05:00
void SeerArrayVisualizerWidget::handleLineTypeButtonGroup () {
handleDataChanged();
}