自定义QTableView网格样式

时间:2019-06-07 09:27:46

标签: c++ qt

我想知道几件事。我已将QTableView细分为一个自定义表格。我想能够做几件事。

首先,我希望选定的单元格不都具有“选定的”颜色(默认为蓝色),而是在选定的单元格周围有一个框(就像在Excel中一样)。为此,我使用了以下内容(在我的自定义QItemDelegate中):

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QModelIndex upIndex = index.sibling(index.row() - 1, index.column());
    QModelIndex downIndex = index.sibling(index.row() + 1, index.column());
    QModelIndex rightIndex = index.sibling(index.row(), index.column() + 1);
    QModelIndex leftIndex = index.sibling(index.row(), index.column() - 1);

    auto newOption = option;
    if (option.state.testFlag(QStyle::State_Selected))
    {
        painter->save();

        auto selIndexes = selM->selectedIndexes().toSet();
        painter->setPen(QPen(Qt::red, 5));
        if (!selIndexes.contains(rightIndex))
            painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
        if (!selIndexes.contains(upIndex))
            painter->drawLine(option.rect.topLeft(), option.rect.topRight());
        if (!selIndexes.contains(downIndex))
            painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
        if (!selIndexes.contains(leftIndex))
            painter->drawLine(option.rect.topLeft(), option.rect.bottomLeft());

        painter->restore();
        // newOption.palette.setBrush(QPalette::Normal, QPalette::Highlight, index.data(Qt::BackgroundRole).value<QColor>());
        newOption.palette.setBrush(QPalette::Normal, QPalette::Highlight, Qt::gray);
    }
    QStyledItemDelegate::paint(painter, newOption, index);
}

这可能不是最佳选择,但我想先做一些有用的事情。

现在它似乎可以工作了,但是很遗憾,它不会自动重新粉刷。选择单元格时会发生以下情况:

Incorrect

我根本不需要的是什么。我猜它没有被重新粉刷,因为(1)区域内的点仍然是红色的;(2)如果我调整窗口的大小,则会得到以下信息:

Kind of correct

哪个更接近我想要达到的目标。

我已经尝试在QTableView中进行此操作:

// selModel is my selection model
connect(selModel, &QItemSelectionModel::selectionChanged, [this]() {
    for(const auto & selected : selModel->selectedIndexes())
    {
        update(visualRect(selected));
        repaint(visualRect(selected));
    }
}

(之前,我实际上使用过setDirtyRegion,但是它也不起作用,所以我认为我会做更多的事情……很残酷。)

最后,我还有一个问题:为什么在单元格角处出现那些奇怪的红色“小线”?即使在“某种”正确的屏幕截图上,我也得到了我无法解释的这些行:

Small lines

如果有任何问题,请提出建议。

1 个答案:

答案 0 :(得分:3)

问题可以很容易地解释如下。

假定已选择单元格(0,0)。现在,用户选择其他 单元格(0,1),(1,0)和(1,1)。 Qt正确地重画了另外三个单元格, 被选中。但是,它不会重新绘制单元格(0,0),因为它既没有 选择或取消选择。当然,对于您想要的行为,您仍然需要重绘此单元格。

只需重绘当前选择的所有索引即可轻松实现。

MyDelegate.h

#pragma once

#include <QStyledItemDelegate>
#include <QItemSelectionModel>

class MyDelegate : public QStyledItemDelegate {

public:
    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

    void setSelectionModel(QItemSelectionModel* selectionModel);
private:
    QItemSelectionModel* mSelectionModel{ nullptr };
};

MyDelegate.cpp

#include "MyDelegate.h"
#include <QPainter>
#include <QDebug>

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (!mSelectionModel) return;
    auto newOption = option;
    auto normalText = newOption.palette.brush(QPalette::ColorGroup::Normal, QPalette::ColorRole::Text);
    newOption.palette.setBrush(QPalette::ColorGroup::Normal, QPalette::ColorRole::Highlight, QBrush(Qt::GlobalColor::blue, Qt::BrushStyle::NoBrush));
    newOption.palette.setBrush(QPalette::ColorGroup::Normal, QPalette::ColorRole::HighlightedText, normalText);
    QStyledItemDelegate::paint(painter, newOption, index);
    QModelIndex upIndex = index.sibling(index.row() - 1, index.column());
    QModelIndex downIndex = index.sibling(index.row() + 1, index.column());
    QModelIndex rightIndex = index.sibling(index.row(), index.column() + 1);
    QModelIndex leftIndex = index.sibling(index.row(), index.column() - 1);
    //auto newOption = option;
    //newOption.palette.setBrush(QPalette::Normal, QPalette::Highlight, Qt::transparent);
    if (option.state.testFlag(QStyle::State_Selected))
    {
        painter->save();
        painter->setClipRect(option.rect);
        auto selIndexes = mSelectionModel->selectedIndexes().toSet();
        painter->setPen(QPen(Qt::red, 5));
        if (!selIndexes.contains(rightIndex))
            painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
        if (!selIndexes.contains(upIndex))
            painter->drawLine(option.rect.topLeft(), option.rect.topRight());
        if (!selIndexes.contains(downIndex))
            painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
        if (!selIndexes.contains(leftIndex))
            painter->drawLine(option.rect.topLeft(), option.rect.bottomLeft());

        painter->restore();
    }
}

void MyDelegate::setSelectionModel(QItemSelectionModel* selectionModel)
{
    mSelectionModel=selectionModel;
}

main.cpp

#include <QApplication>
#include <QDebug>
#include <QStandardItemModel>
#include <QTableWidget>
#include "MyDelegate.h"

int main(int argc, char** args) {
    QApplication app(argc, args);
    auto widget = new QTableView;
    QStandardItemModel model;
    model.setRowCount(10);
    model.setColumnCount(10);
    for (auto i = 0; i < 10; i++) {
        for (auto j = 0; j < 10; j++) {
            model.setItem(i, j, new QStandardItem("Test"));
        }
    }
    auto selModel = new QItemSelectionModel;
    selModel->setModel(&model);
    widget->setModel(&model);
    widget->setSelectionModel(selModel);

    auto delegate = new MyDelegate;
    delegate->setSelectionModel(selModel);
    widget->setItemDelegate(delegate);
    // Ensures that also items are repainted, that where neither selected nor deselect, but will stay selected
    // This solutions eventually paints elements twice
    QObject::connect(selModel, &QItemSelectionModel::selectionChanged, [widget,selModel](auto &selected, auto& deselected) {
        for (auto item : selModel->selectedIndexes()) {
            widget->update(item); 
        }
    });
    widget->show();
    app.exec();
}

关于奇怪的红线伪像,我应该在允许的矩形内绘制红线。这可以通过裁剪项目边界来轻松实现。