如何在 table 个视图之间共享选择?
How to share a selection between table views?
我正在编写一个 Qt C++ 应用程序来显示字节值及其旁边的 hexdump。为此,我正在创建一个新对话框,我有 2 个 QTableView(每个用于字节表示或 hexdump)和 1 个 class 继承自 QAbstractTableModel 和 1 个 class 继承自 QStyledItemDelegate。
现在我想添加功能,当我在第一个 QTableView 中 select 某些内容时,它也会在第二个 QTableView 中 selected。
在 Qt 关于 model-view 编程的文档中,他们使用命令 secondTableView->setSelectionModel(firstTableView->selectionModel())
做了类似的事情,但这对我不起作用。我还使用 paint
方法使用我的委托(现在处于原型阶段)突出显示单元格。难道是因为这个?
我的代码:
对话:
#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);
}
型号:
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 '[=12=]' 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();
}
代表:
#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);
}
编辑:
所以我尝试将插槽 updateSelection
添加到我的对话框中,并将它连接到我的两个 DataViewerModel
实例的 selectionModel
,如下所示:
我在对话框 DataViewer 构造函数中连接它们:
connect(byteTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
connect(hexTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
我的 updateSelection
看起来像这样:
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);
}
}
当然,我在 DataViewer
header 中添加了 private slots : void updateSelection(const QItemSelection& selected, const QItemSelection& deselected);
。不幸的是,这也不起作用。还有其他建议吗?
如果您的 2 个模型实例没有相同的数据,它将无法工作。
我将采用更简单的方法,使用连接到您选择的模型之一的自定义插槽。
connect(byteTableView->selectionModel(), &SelectionModel::selectionChanged, this, &DataViewer::updateSelection);
并在您的更新选择中:
void DataViewer::updateSelection(const QItemSelection& selected, const QItemSelection& deselected)
{
QModelIndexList items = selected.indexes();
auto oppositeSelectionModel = getOppositeSelectionModel();
for (const QModelIndex& index : qAsConst(items))
{
oppositeSelectionModel->select(index);
}
}
现在对于你的最后一个问题,你的单元格的颜色与复选框的状态相关联
if (dataHighLighter->isChecked())
我想你想知道这个索引是否属于 selectionModel,我相信你能想出一些东西。
我正在编写一个 Qt C++ 应用程序来显示字节值及其旁边的 hexdump。为此,我正在创建一个新对话框,我有 2 个 QTableView(每个用于字节表示或 hexdump)和 1 个 class 继承自 QAbstractTableModel 和 1 个 class 继承自 QStyledItemDelegate。
现在我想添加功能,当我在第一个 QTableView 中 select 某些内容时,它也会在第二个 QTableView 中 selected。
在 Qt 关于 model-view 编程的文档中,他们使用命令 secondTableView->setSelectionModel(firstTableView->selectionModel())
做了类似的事情,但这对我不起作用。我还使用 paint
方法使用我的委托(现在处于原型阶段)突出显示单元格。难道是因为这个?
我的代码:
对话:
#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);
}
型号:
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 '[=12=]' 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();
}
代表:
#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);
}
编辑:
所以我尝试将插槽 updateSelection
添加到我的对话框中,并将它连接到我的两个 DataViewerModel
实例的 selectionModel
,如下所示:
我在对话框 DataViewer 构造函数中连接它们:
connect(byteTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
connect(hexTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection(QItemSelection, QItemSelection)));
我的 updateSelection
看起来像这样:
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);
}
}
当然,我在 DataViewer
header 中添加了 private slots : void updateSelection(const QItemSelection& selected, const QItemSelection& deselected);
。不幸的是,这也不起作用。还有其他建议吗?
如果您的 2 个模型实例没有相同的数据,它将无法工作。
我将采用更简单的方法,使用连接到您选择的模型之一的自定义插槽。
connect(byteTableView->selectionModel(), &SelectionModel::selectionChanged, this, &DataViewer::updateSelection);
并在您的更新选择中:
void DataViewer::updateSelection(const QItemSelection& selected, const QItemSelection& deselected)
{
QModelIndexList items = selected.indexes();
auto oppositeSelectionModel = getOppositeSelectionModel();
for (const QModelIndex& index : qAsConst(items))
{
oppositeSelectionModel->select(index);
}
}
现在对于你的最后一个问题,你的单元格的颜色与复选框的状态相关联
if (dataHighLighter->isChecked())
我想你想知道这个索引是否属于 selectionModel,我相信你能想出一些东西。