Qt QTableView - 当使用IsUserCheckable时复选框的对齐方式

11

我正在使用 QTableView 的复选框标记 Qt::ItemIsUserCheckable 来在表格单元格中显示复选框。

尝试通过阅读有关对齐方式的一些内容来居中显示单元格中的复选框,因此我从模型的 data() 函数返回 Qt::AlignCenter 作为 TextAlignmentRole。

QVariant ExampleModel::data(const QModelIndex &index, int role) const 
{
  if(!index.isValid())
     return QVariant();

  if (role == Qt::TextAlignmentRole) 
       return Qt::AlignCenter | Qt::AlignVCenter;
}

然而这并没有对齐我的复选框。

有人知道如何在此模式下对齐复选框吗?

7个回答

4
在进一步研究委托选项后,我找到了一个很好的参考资料(不幸的是现在已不可用),并提出了以下混合方案,使用QItemDelegate和IsUserCheckable。
基本上,您需要扩展QItemDelegate,并重新实现使用drawCheck函数来居中和使用editorEvent处理鼠标和键盘事件,同时设置具有适当状态的模型。
void drawCheck(QPainter* painter, QStyleOptionViewItem const& option, QRect const& rect, Qt::CheckState state) const

并且

bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)

同时可以参考这个类似的问题链接...


2个链接已经失效。 - Steve Lorimer

3

Python(PySide,PyQt)的解决方案以使复选框居中,并允许进行编辑:

class BooleanDelegate(QItemDelegate):

    def __init__(self, *args, **kwargs):
        super(BooleanDelegate, self).__init__(*args, **kwargs)

    def paint(self, painter, option, index):
        # Depends on how the data function of your table model is implemented
        # 'value' should recive a bool indicate if the checked value.
        value = index.data(Qt.CheckStateRole)  
        self.drawCheck(painter, option, option.rect, value)
        self.drawFocus(painter, option, option.rect)

    def editorEvent(self, event, model, option, index):
        if event.type() == QEvent.MouseButtonRelease:
            value = bool(model.data(index, Qt.CheckStateRole))
            model.setData(index, not value)
            event.accept()
        return super(BooleanDelegate, self).editorEvent(event, model, option, index)

在您的表格模型中,确保标记允许用户勾选/取消勾选单元格。
class MyTableModel(QAbstractTableModel):

    ...

    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        if index.column() in self.columns_boolean:
            return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable
        return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)

最后,在您的表格中设置BooleanDelagate

self.boolean_delegate = BooleanDelegate()
self.input_gui.setItemDelegateForColumn(5, self.boolean_delegate)

2

也许不是你想要的答案,但我发现在使用qtableviews时,实现自己的复选框项目代理要容易得多。


2

1
这是我想出的解决方案。假设您希望复选框是单元格中唯一的内容。
class CenteredCheckboxDelegate final : public QStyledItemDelegate
{
public:
    using QStyledItemDelegate::QStyledItemDelegate;

    void paint(QPainter * painter, const QStyleOptionViewItem & o, const QModelIndex & index ) const override
    {
        auto option2 = o;
        initStyleOption(&option2, index);

        auto * widget = option2.widget;
        auto * style = widget ? widget->style() : QApplication::style();

        // Turn off all features and just draw the background
        option2.state.setFlag(QStyle::State_HasFocus, false);
        option2.features.setFlag(QStyleOptionViewItem::HasDisplay, false);
        option2.features.setFlag(QStyleOptionViewItem::HasDecoration, false);
        option2.features.setFlag(QStyleOptionViewItem::HasCheckIndicator, false);
        style->drawControl(QStyle::CE_ItemViewItem, &option2, painter, widget);

        // Then just draw the a checkbox centred in the cell
        option2.rect = getCheckboxRect(option2);
        auto stateFlag = option2.checkState == Qt::Checked ? QStyle::State_On : QStyle::State_Off;
        option2.state.setFlag(stateFlag, true);
        style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option2, painter, widget);
    }

    bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override
    {
        auto flags = index.flags();
        if (!flags.testFlag(Qt::ItemIsUserCheckable) || !flags.testFlag(Qt::ItemIsEnabled))
        {
            return false;
        }

        if(event->type() == QEvent::MouseButtonRelease)
        {
            auto * mouseEvent = static_cast<QMouseEvent*>(event);
            bool mouseOverCheckbox = getCheckboxRect(option).contains(mouseEvent->pos());
            if(!mouseOverCheckbox) return false;
        }
        else if(event->type() == QEvent::KeyPress)
        {
            auto * keyEvent = static_cast<QKeyEvent*>(event);
            if(keyEvent->key() != Qt::Key_Space) return false;
        }
        else
        {
            return false;
        }

        auto checkState = index.data(Qt::CheckStateRole).value<Qt::CheckState>();
        auto toggledCheckState = checkState == Qt::Checked ? Qt::Unchecked : Qt::Checked;
        return model->setData(index, toggledCheckState, Qt::CheckStateRole);
    }

private:
    QRect getCheckboxRect(const QStyleOptionViewItem & option) const
    {
        auto * widget = option.widget;
        auto * style = widget ? widget->style() : QApplication::style();
        auto checkboxSize = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option, widget).size();
        return QStyle::alignedRect(option.direction, Qt::AlignCenter, checkboxSize, option.rect);
    }
};


0

基于Parker Coates惊人的C++版本,使用PyQt5 / Pyside2版本

class QTableviewEditableDelegate(QStyledItemDelegate):

def __init__(self, alignment: Qt.Alignment, parent: Optional[QWidget] = None):
    super().__init__(parent)
    self.alignment: Qt.Alignment = alignment
    self.parent = parent

    if self.parent:
        self.style = self.parent.style()
    else:
        self.style = QApplication.style()

def editorEvent(self, event: QMouseEvent, model: QAbstractItemModel, option: QStyleOptionViewItem,
                index: QModelIndex) -> bool:
    checkbox_data = index.data(Qt.CheckStateRole)
    flags = index.flags()
    if not (flags & Qt.ItemIsUserCheckable) or not (flags & Qt.ItemIsEnabled) or checkbox_data is None:
        return False
    else:
        if event.type() == QEvent.MouseButtonRelease:
            mouseover_checkbox: bool = self.get_checkbox_rect(option).contains(event.pos())
            if not mouseover_checkbox:
                return False
        elif event.type() == QEvent.KeyPress and event.key() != Qt.Key_Space:
            return False
        else:
            return False
        if checkbox_data == Qt.Checked:
            checkbox_toggled: int = Qt.Unchecked
        else:
            checkbox_toggled: int = Qt.Checked
        return model.setData(index, checkbox_toggled, Qt.CheckStateRole)

def get_checkbox_rect(self, option: QStyleOptionViewItem) -> QRect:
    widget = option.widget
    if widget:
        style = widget.style()
    else:
        style = self.style()
    checkbox_size: QSize = style.subElementRect(QStyle.SE_CheckBoxIndicator, option, widget).size()
    return QStyle.alignedRect(option.direction, Qt.AlignCenter, checkbox_size, option.rect)

def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
    try:
        self.initStyleOption(option, index)
        painter.save()

        flags: Qt.ItemFlags = index.model().flags(index)
        widget: Optional[QWidget] = option.widget
        checkbox_data = index.data(Qt.CheckStateRole)
        if widget:
            style = widget.style()
        else:
            style = self.style()

        if option.HasCheckIndicator and checkbox_data is not None:
            option_checkbox = option
            self.initStyleOption(option_checkbox, index)
            option_checkbox.state = option_checkbox.state & ~QStyle.State_HasFocus
            option_checkbox.features = option_checkbox.features & ~QStyleOptionViewItem.HasDisplay
            option_checkbox.features = option_checkbox.features & ~QStyleOptionViewItem.HasDecoration
            option_checkbox.features = option_checkbox.features & ~QStyleOptionViewItem.HasCheckIndicator
            style.drawControl(QStyle.CE_ItemViewItem, option_checkbox, painter, widget)

            # Then just draw the a checkbox centred in the cell
            option_checkbox.rect = self.get_checkbox_rect(option_checkbox)
            if option_checkbox.checkState == Qt.Checked:
                state_flag = QStyle.State_On
            else:
                state_flag = QStyle.State_Off

            option_checkbox.state = option_checkbox.state | state_flag
            style.drawPrimitive(QStyle.PE_IndicatorViewItemCheck, option_checkbox, painter, widget)

        else:
            QStyledItemDelegate.paint(self, painter, option, index)
       
        painter.restore()

    except Exception as e:
        print(repr(e))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接