如何在 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,我相信你能想出一些东西。