3

我有一个QTableView带有自定义的自定义模型,QHeaderView以便呈现一个用于对表格内容执行“全选”功能的复选框。

在我的标头的重载paintSection()函数中,我成功渲染了用于全选的复选框:

QStyleOptionButton option;
option.rect = QRect(3,10,16,16);
option.state = QStyle::State_Enabled | QStyle::State_Active;
if (isChecked_)
    option.state |= QStyle::State_On;
else
    option.state |= QStyle::State_Off;
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter);

不幸的是,复选框没有居中呈现,而是左对齐。QStyledItemDelegate这与每个表条目的列中正确居中的复选框完全冲突。

我知道我可以更改前两个参数QRect来更改绘制图元的原点,但这对列宽的变化没有响应。虽然,固定列宽并不是最糟糕的解决方案。

如何正确地将列标题中的复选框居中?

辅助问题:标题中的复选框可以通过单击单元格中的任意位置来切换而不仅仅是在框本身上(与通过代表在表格中呈现的那些不同)。有没有办法来解决这个问题?

4

2 回答 2

3

@scopchanov 的解决方案对我不起作用,因为复选框涵盖了标题的整个项目

在此处输入图像描述 在此处输入图像描述

一种可能的解决方案是绘制带有样式的 CheckBox,但除此之外,您必须记住该元素是否被选中,为此我们使用QMap<>第一个元素,logicalIndex因为即使移动它也不会改变列和第二个元素是状态。

#include <QApplication>
#include <QHeaderView>
#include <QMouseEvent>
#include <QPainter>
#include <QStandardItemModel>
#include <QTableView>

class CheckedHeaderView : public QHeaderView
{
    Q_OBJECT
public:
    using QHeaderView::QHeaderView;
protected:
    void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override
    {
        painter->save();
        QHeaderView::paintSection(painter, rect, logicalIndex);
        painter->restore();
        QStyleOptionButton opt;

        QRect checkbox_rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt);
        checkbox_rect.moveCenter(rect.center());
        opt.rect = checkbox_rect;
        opt.state = QStyle::State_Enabled | QStyle::State_Active;
        if(logicalIndex == columnDown)
            opt.state |= QStyle::State_Sunken;
        if (states[logicalIndex])
            opt.state |= QStyle::State_On;
        else
            opt.state |= QStyle::State_Off;
        style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, painter);
    }
    void mousePressEvent(QMouseEvent *event) override
    {
        QHeaderView::mousePressEvent(event);
        int li = logicalIndexAt(event->pos());
        if(li == -1) return;
        columnDown = li;
        updateSection(li);
    }
    void mouseReleaseEvent(QMouseEvent *event) override
    {
        QHeaderView::mouseReleaseEvent(event);
        int li = logicalIndexAt(event->pos());
        if(li == -1) return;
        states[li] = !states[li];
        Q_EMIT checked(li, states[li]);
        columnDown = -1;
        updateSection(li);
    }
Q_SIGNALS:
    void checked(int logicalIndex, bool state);
private:
    QMap<int, bool> states;
    int columnDown = -1;
};

class TableView : public QTableView
{
    Q_OBJECT
public:
    TableView(QWidget *parent = nullptr):
        QTableView(parent)
    {
        CheckedHeaderView *header = new CheckedHeaderView(Qt::Horizontal, this);
        setHorizontalHeader(header);
        connect(header, &CheckedHeaderView::checked, this, &TableView::on_checked);
    }
private Q_SLOTS:
    void on_checked(int logicalIndex, bool state){
        QItemSelectionModel::SelectionFlags command = state? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
        for(int r=0; r < model()->rowCount(); r++){
            QModelIndex ix = model()->index(r, logicalIndex);
            selectionModel()->select(ix, command);
        }
    }
};


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TableView w;
    QStandardItemModel *model = new QStandardItemModel(8, 6, &w);
    w.setModel(model);
    w.show();
    return a.exec();
}

在此处输入图像描述

于 2018-10-04T18:09:23.987 回答
0

解决方案

QHeaderView::paintSection以 aQRect &rect作为参数。设置QStyleOption::rect为等于rect,即替换

option.rect = QRect(3,10,16,16);

option.rect = rect;

注意:此解决方案可响应列宽的变化。

例子

使用您的实现,paintSection我创建了一个最小示例,以演示如何实现建议的解决方案。代码可在GitHub上找到。

结果

提供的示例在 Windows 7 上产生以下结果:

Windows 7 - 在三行表格的标题中带有复选框的窗口

Windows 10 上的结果:

Windows 10 - 在三行表格的标题中带有复选框的窗口

注意:此解决方案不适用于 Linux,因为它会产生以下结果(Ubuntu 17):

Ubuntu 17 - 在三行表的标题中带有复选框的窗口

于 2018-10-03T11:49:15.400 回答