I am writing a Qt C++ app to show byte values and its hexdump right next to it. For that I am creating a new dialog, I have 2 QTableViews(each for byte representation or hexdump) and 1 class which inherits from QAbstractTableModel and 1 class which inherits from QStyledItemDelegate.
Now I would like to add functionality, that when I select something in first QTableView, it will also be selected in second QTableView.
In Qt's documentaion on model-view programming they do something similiar using command secondTableView->setSelectionModel(firstTableView->selectionModel())
But that doesn't work for me. I am also highlighting cells using my delegate(now its just in prototype phase) using paint
method. Could it be because of this ?
My code :
dialog :
#include "dataviewer.h"
DataViewer::DataViewer(QListWidgetItem* item, QCheckBox* dataHighLighter, QByteArray* headerArray, QWidget *parent)
: QDialog(parent)
{
setupUi(this);
byteTableViewer = std::make_unique<DataViewerModel>(item, headerArray, false, this);
hexTableViewer = std::make_unique<DataViewerModel>(item, headerArray, true, this);
dataDelegate = std::make_unique<DataViewerDelegate>(dataHighLighter, this);
InitializeTableViewer(Ui::DataViewer::byteTableView, byteTableViewer.get(), false);
InitializeTableViewer(Ui::DataViewer::hexTableView, hexTableViewer.get(), true);
connect(byteTableView->verticalScrollBar(), SIGNAL(valueChanged(int)), hexTableView->verticalScrollBar(), SLOT(setValue(int)));
connect(hexTableView->verticalScrollBar(), SIGNAL(valueChanged(int)), byteTableView->verticalScrollBar(), SLOT(setValue(int)));
}
void DataViewer::InitializeTableViewer(QTableView* table, DataViewerModel* viewer, bool hexa)
{
table->setModel(viewer);
table->horizontalHeader()->setMinimumSectionSize(5);
table->verticalHeader()->setMinimumSectionSize(5);
table->setItemDelegate(dataDelegate.get());
table->resizeColumnsToContents();
table->resizeRowsToContents();
table->setShowGrid(false);
if (hexa)
table->verticalHeader()->setVisible(false);
table->verticalScrollBar()->setFixedSize(QSize(20, table->height()));
table->horizontalHeader()->setVisible(false);
}
model :
DataViewerModel::DataViewerModel(QListWidgetItem* item, QByteArray* headerArray, bool hexdump, QWidget* parent) : QAbstractTableModel(parent)
{
this->item = item;
this->headerArray.replace(0, headerArray->size() - 1, *headerArray);
this->hexdump = hexdump;
BYTES_ON_ROW = 16;
}
int DataViewerModel::rowCount(const QModelIndex& parent) const
{
QByteArray leftoverData = item->data(TRANSFER_LEFTOVER_DATA).toByteArray();
int size = leftoverData.size() + headerArray.size();
return (size / BYTES_ON_ROW == 0) ? size/BYTES_ON_ROW : (size/BYTES_ON_ROW) + 1; //4B on one line
}
int DataViewerModel::columnCount(const QModelIndex& parent) const
{
return BYTES_ON_ROW; //16B on one line
}
QVariant DataViewerModel::data(const QModelIndex& index, int role) const
{
QByteArray leftoverData = item->data(TRANSFER_LEFTOVER_DATA).toByteArray();
int size = leftoverData.size() + headerArray.size() - 2; //-2 for both '\0' in byteArrays
int ind = (index.row() * BYTES_ON_ROW) + index.column();
QDataStream stream1(leftoverData);
QDataStream stream2(headerArray);
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role == Qt::DisplayRole)
{
if (!index.isValid())
{
return QVariant();
}
if (ind > size + 1)
{
return QVariant();
}
else
{
std::stringstream stream;
if (ind < headerArray.size())
{
int hex = headerArray.at(ind);
hex = (hex < 0) ? hex + 256 : hex;
if (hexdump)
{
if (hex < 0x20 || hex > 0x7e)
{
return QVariant(".");
}
else
{
return QVariant(QChar(hex));
}
}
stream << std::uppercase << std::setw(2) << std::setfill('0') << std::hex << hex;
return QVariant(stream.str().c_str());
}
else
{
int hex = leftoverData.at(ind - headerArray.size());
hex = (hex < 0) ? hex + 256 : hex;
if (hexdump)
{
if (hex < 0x20 || hex > 0x7e)
{
return QVariant(".");
}
else
{
return QVariant(QChar(hex));
}
}
stream << std::uppercase << std::setw(2) << std::setfill('0') << std::hex << hex;
return QVariant(stream.str().c_str());
}
}
}
else
{
return QVariant();
}
}
QVariant DataViewerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical && role == Qt::DisplayRole)
{
std::stringstream stream;
stream << std::uppercase << std::setw(6) << std::setfill('0') << std::hex << section;
return QVariant(stream.str().c_str());
}
return QVariant();
}
delegate :
#include "dataviewerdelegate.h"
DataViewerDelegate::DataViewerDelegate(QCheckBox* dataHighLighter, QObject *parent)
: QStyledItemDelegate(parent)
{
this->dataHighLighter = dataHighLighter;
headerColor = headerColor.red();
additionalDataColor = additionalDataColor.green();
leftoverDataColor = leftoverDataColor.blue();
}
DataViewerDelegate::~DataViewerDelegate()
{
}
void DataViewerDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if (dataHighLighter->isChecked())
{
auto a = (index.row() * 4) + index.column();
if (a > 5)
{
QColor background = QColor(135, 206, 255);
painter->fillRect(option.rect, background);
}
else
{
QColor background = QColor(25, 123, 145);
painter->fillRect(option.rect, background);
}
}
QStyledItemDelegate::paint(painter, option, index);
}
QSize DataViewerDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QSize(30, 30);
}
EDIT:
So I've tried adding slot updateSelection
to my dialog, and connect it selectionModel
of both of my instances of DataViewerModel
like this :
I am connecting them in dialogs DataViewer constructor :
connect(byteTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
connect(hexTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
And my updateSelection
looks like this :
void DataViewer::updateSelection(const QItemSelection& selected, const QItemSelection& deselected)
{
QModelIndexList items = selected.indexes();
for (const QModelIndex& index : qAsConst(items))
{
QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
byteTableViewer->setData(index, text);
hexTableViewer->setData(index, text);
}
}
Of course I've added private slots : void updateSelection(const QItemSelection& selected, const QItemSelection& deselected);
in my DataViewer
header. Unfortunately, this didn't work either. Are there any other suggestions what can I do ?