如何将原始复选框与 QHeaderView 列 header 的中心对齐?
How to align a primitive checkbox with the center of QHeaderView's column header?
我有一个带有自定义 QHeaderView
的自定义 QTableView
模型,以便呈现用于对 table 的内容执行 "select all" 功能的复选框。
在我的 header 重载的 paintSection()
函数中,我成功渲染了用于 select 的复选框:
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);
不幸的是,复选框未呈现为居中,而是左对齐。这与列中每个 table 条目的 properly-centered QStyledItemDelegate
复选框完全冲突。
我知道我可以更改 QRect
的前两个参数来更改绘制图元的原点,但这对列宽的变化没有反应。虽然,固定列宽并不是最糟糕的解决方案。
如何正确地将复选框置于 header 列的中心?
辅助问题:header 中的复选框可以通过单击单元格中的 任意位置 来切换,而不仅仅是在框本身上(不同于在table 通过代表)。有办法解决这个问题吗?
解决方案
QHeaderView::paintSection
takes a QRect &rect
as an argument. Set QStyleOption::rect
等于rect
,即替换
option.rect = QRect(3,10,16,16);
与
option.rect = rect;
注意:此解决方案响应列宽的变化。
例子
使用您对 paintSection
的实施,我创建了一个最小示例,以演示如何实施建议的解决方案。该代码可在 GitHub.
上获得
结果
提供的示例在 Windows 7 上产生以下结果:
Windows10 的结果:
注意: 此解决方案不适用于 Linux,因为它会产生以下结果 (Ubuntu 17):
The solution of @scopchanov 对我不起作用,因为复选框覆盖了 header
的整个项目
一个可能的解决方案是用样式绘制 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();
}
我有一个带有自定义 QHeaderView
的自定义 QTableView
模型,以便呈现用于对 table 的内容执行 "select all" 功能的复选框。
在我的 header 重载的 paintSection()
函数中,我成功渲染了用于 select 的复选框:
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);
不幸的是,复选框未呈现为居中,而是左对齐。这与列中每个 table 条目的 properly-centered QStyledItemDelegate
复选框完全冲突。
我知道我可以更改 QRect
的前两个参数来更改绘制图元的原点,但这对列宽的变化没有反应。虽然,固定列宽并不是最糟糕的解决方案。
如何正确地将复选框置于 header 列的中心?
辅助问题:header 中的复选框可以通过单击单元格中的 任意位置 来切换,而不仅仅是在框本身上(不同于在table 通过代表)。有办法解决这个问题吗?
解决方案
QHeaderView::paintSection
takes a QRect &rect
as an argument. Set QStyleOption::rect
等于rect
,即替换
option.rect = QRect(3,10,16,16);
与
option.rect = rect;
注意:此解决方案响应列宽的变化。
例子
使用您对 paintSection
的实施,我创建了一个最小示例,以演示如何实施建议的解决方案。该代码可在 GitHub.
结果
提供的示例在 Windows 7 上产生以下结果:
Windows10 的结果:
注意: 此解决方案不适用于 Linux,因为它会产生以下结果 (Ubuntu 17):
The solution of @scopchanov 对我不起作用,因为复选框覆盖了 header
的整个项目一个可能的解决方案是用样式绘制 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();
}