将QCheckBox放置在QListView中项目的左上方

时间:2019-09-04 17:59:12

标签: python pyside

如何在左上方的QListView中放置项目的复选框?我尝试使用样式表,但是当我调整项目大小时,复选框未保持其在左上方的位置。它被隐藏了。

有人对如何实现这一目标有任何建议吗?我可以使用QItemDelegate或QStyledItemDelegate解决我的问题吗?

我的目标是使它看起来像这样...

enter image description here

import os, sys, re
from Qt import QtWidgets, QtGui, QtCore
from PictureShop.Resources import StyleUtils


################################################################################
# Dummy Values
################################################################################
values = ['MomBod','Colonel','Tater','Tot','Ginger','Donut','Sport','LaLa','Itchy','Bruiser','Cotton','Cumulus','Toodles']


################################################################################
# Widgets
################################################################################
class ViewerWidget(QtWidgets.QWidget):

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.resize(500,500)

        self.uiIconSize = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.uiIconSize.setRange(32,256)
        self.uiIconSize.setValue(96)
        self.uiIconSize.setMinimumWidth(100)

        self.viewerModel = QtGui.QStandardItemModel()
        self.uiListView = QtWidgets.QListView()
        self.uiListView.setSpacing(5)
        self.uiListView.setMovement(QtWidgets.QListView.Static)
        self.uiListView.setViewMode(QtWidgets.QListView.IconMode)
        self.uiListView.setLayoutMode(QtWidgets.QListView.Batched)
        self.uiListView.setBatchSize(100)
        self.uiListView.setFlow(QtWidgets.QListView.LeftToRight)
        self.uiListView.setWrapping(True)
        self.uiListView.setResizeMode(QtWidgets.QListView.Adjust)
        self.uiListView.setDragEnabled(False)
        self.uiListView.setUniformItemSizes(True)
        self.uiListView.setSelectionMode(QtWidgets.QListView.ExtendedSelection)
        self.uiListView.setModel(self.viewerModel)

        # layout
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.uiListView)
        self.layout.addWidget(self.uiIconSize)
        self.setLayout(self.layout)

        # Signals
        self.uiIconSize.valueChanged.connect(self.slotChangedIconSize)
        self.uiIconSize.setValue(96)
        self.uiListView.setIconSize(QtCore.QSize(96,96))

        # Init
        self.populateModel()
        self.setStyleSheet('''
            QWidget::indicator {
                subcontrol-position: top center;
                position: relative;
                left: 30px;
                top: -20px;
            }

        ''')


    # Methods
    def populateModel(self):
        model = self.viewerModel
        model.clear()

        icon = QtGui.QIcon('C:/Users/jmartini/Desktop/brokenImage.png')
        for x in values:
            newItem = QtGui.QStandardItem()
            newItem.setCheckable(True)
            newItem.setData(icon, role=QtCore.Qt.DecorationRole)
            model.appendRow(newItem)


    # Slots
    def slotChangedIconSize(self):
        size = self.uiIconSize.value()
        self.uiListView.setIconSize(QtCore.QSize(size,size))


################################################################################
# Unit Testing
################################################################################
def test_ViewerWidget():
    app = QtWidgets.QApplication(sys.argv)
    app.setOrganizationName('mine')
    app.setApplicationName('browser')
    ex = ViewerWidget()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    pass
    test_ViewerWidget()

从以下答案中获取帮助。我这里显示一个错误:

enter image description here

根据以下评论提供可行的解决方案:

import os, sys, re
from Qt import QtWidgets, QtGui, QtCore


################################################################################
# Dummy Values
################################################################################
values = ['MomBod','Colonel','Tater','Tot','Ginger','Donut','Sport','LaLa','Itchy','Bruiser','Cotton','Cumulus','Toodles']



class ItemDelegate(QtWidgets.QStyledItemDelegate):

    def __init__(self, parent=None, *args):
        QtWidgets.QStyledItemDelegate.__init__(self, parent, *args)


    # overrides
    def sizeHint(self, option, index):
        isw, ish = option.decorationSize.toTuple()
        return QtCore.QSize(isw, ish)


    def getCheckboxRect(self, option):
        return QtCore.QRect(4, 4, 18, 18).translated(option.rect.topLeft())


    def paint(self, painter, option, index):
        painter.save()

        # Draw
        isw, ish = option.decorationSize.toTuple()
        x, y, dx, dy = option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height()

        # Decoration
        pic = index.data(QtCore.Qt.DecorationRole)
        if pic:
            painter.drawPixmap(x, y, pic.pixmap(isw, ish))

        # Indicate Selected
        painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
        if option.state & QtWidgets.QStyle.State_Selected:
            painter.setBrush(QtGui.QBrush(QtGui.QColor(0,70,240,128)))
        else:
            painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
        painter.drawRect(QtCore.QRect(x, y, dx, dy))

        # Checkstate
        value = index.data(QtCore.Qt.CheckStateRole)
        if value is not None:
            opt = QtWidgets.QStyleOptionViewItem()
            opt.rect = self.getCheckboxRect(option)
            opt.state = opt.state & ~QtWidgets.QStyle.State_HasFocus
            if value == QtCore.Qt.Unchecked:
                opt.state |= QtWidgets.QStyle.State_Off
            elif value == QtCore.Qt.PartiallyChecked:
                opt.state |= QtWidgets.QStyle.State_NoChange
            elif value == QtCore.Qt.Checked:
                opt.state = QtWidgets.QStyle.State_On
            style = QtWidgets.QApplication.style()
            style.drawPrimitive(
                QtWidgets.QStyle.PE_IndicatorViewItemCheck, opt, painter, None
            )

        painter.restore()


    def editorEvent(self, event, model, option, index):
        flags = model.flags(index)
        if (
            not (flags & QtCore.Qt.ItemIsUserCheckable)
            or not (option.state & QtWidgets.QStyle.State_Enabled)
            or not (flags & QtCore.Qt.ItemIsEnabled)
        ):
            return False

        value = index.data(QtCore.Qt.CheckStateRole)
        if value is None:
            return False

        style = QtWidgets.QApplication.style()
        if event.type() in (
            QtCore.QEvent.MouseButtonRelease,
            QtCore.QEvent.MouseButtonDblClick,
            QtCore.QEvent.MouseButtonPress,
        ):
            viewOpt = QtWidgets.QStyleOptionViewItem(option)
            self.initStyleOption(viewOpt, index)
            checkRect = self.getCheckboxRect(viewOpt)
            if event.button() != QtCore.Qt.LeftButton or not checkRect.contains(
                event.pos()
            ):
                return False
            if event.type() in (
                QtCore.QEvent.MouseButtonPress,
                QtCore.QEvent.MouseButtonDblClick,
            ):
                return True
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.key() not in (QtCore.Qt.Key_Space, QtCore.Qt.Key_Select):
                return False
        else:
            return False
        state = value
        if flags & QtCore.Qt.ItemIsTristate:
            state = QtCore.Qt.CheckState((state + 1) % 3)
        else:
            state = (
                QtCore.Qt.Unchecked if state == QtCore.Qt.Checked else QtCore.Qt.Checked
            )
        return model.setData(index, state, QtCore.Qt.CheckStateRole)

################################################################################
# Widgets
################################################################################
class ViewerWidget(QtWidgets.QWidget):

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.resize(500,500)

        self.uiIconSize = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.uiIconSize.setRange(32,256)
        self.uiIconSize.setValue(96)
        self.uiIconSize.setMinimumWidth(100)

        self.viewerModel = QtGui.QStandardItemModel()
        self.uiListView = QtWidgets.QListView()
        self.uiListView.setSpacing(5)
        self.uiListView.setMovement(QtWidgets.QListView.Static)
        self.uiListView.setViewMode(QtWidgets.QListView.IconMode)
        self.uiListView.setLayoutMode(QtWidgets.QListView.Batched)
        self.uiListView.setBatchSize(100)
        self.uiListView.setFlow(QtWidgets.QListView.LeftToRight)
        self.uiListView.setWrapping(True)
        self.uiListView.setResizeMode(QtWidgets.QListView.Adjust)
        self.uiListView.setDragEnabled(False)
        self.uiListView.setUniformItemSizes(True)
        self.uiListView.setSelectionMode(QtWidgets.QListView.ExtendedSelection)
        self.uiListView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.uiListView.setModel(self.viewerModel)
        self.uiListView.setItemDelegate(ItemDelegate())

        # layout
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.uiListView)
        self.layout.addWidget(self.uiIconSize)
        self.setLayout(self.layout)

        # Signals
        self.uiIconSize.valueChanged.connect(self.slotChangedIconSize)
        self.uiIconSize.setValue(96)
        self.uiListView.setIconSize(QtCore.QSize(96,96))

        # Init
        self.populateModel()


    # Methods
    def populateModel(self):
        model = self.viewerModel
        model.clear()



        icon = QtGui.QIcon("C:/Users/JokerMartini-Asus/Desktop/thumbnail_image.png")
        for x in values:
            newItem = QtGui.QStandardItem()
            newItem.setCheckable(True)
            newItem.setData(icon, role=QtCore.Qt.DecorationRole)
            model.appendRow(newItem)


    # Slots
    def slotChangedIconSize(self):
        size = self.uiIconSize.value()
        self.uiListView.setIconSize(QtCore.QSize(size,size))


################################################################################
# Unit Testing
################################################################################
def test_ViewerWidget():
    app = QtWidgets.QApplication(sys.argv)
    app.setOrganizationName('mine')
    app.setApplicationName('browser')
    ex = ViewerWidget()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    pass
    test_ViewerWidget()

1 个答案:

答案 0 :(得分:1)

使用委托可以在绘制图标后绘制:

class Delegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(Delegate, self).initStyleOption(option, index)
        option.features &= ~QtWidgets.QStyleOptionViewItem.HasCheckIndicator

    def paint(self, painter, option, index):
        super(Delegate, self).paint(painter, option, index)
        value = index.data(QtCore.Qt.CheckStateRole)
        if value is not None:
            opt = QtWidgets.QStyleOptionViewItem()
            opt.rect = self.checkRect(option)
            opt.state = opt.state & ~QtWidgets.QStyle.State_HasFocus
            if value == QtCore.Qt.Unchecked:
                opt.state |= QtWidgets.QStyle.State_Off
            elif value == QtCore.Qt.PartiallyChecked:
                opt.state |= QtWidgets.QStyle.State_NoChange
            elif value == QtCore.Qt.Checked:
                opt.state = QtWidgets.QStyle.State_On
            widget = option.widget
            style = QtWidgets.QApplication.style() if widget is None else widget.style()
            style.drawPrimitive(
                QtWidgets.QStyle.PE_IndicatorViewItemCheck, opt, painter, widget
            )

    def checkRect(self, option):
        height = option.rect.height()
        x, y, w, h = (f * height for f in (0.15, 0.15, 0.2, 0.2))
        return QtCore.QRect(x, y, w, h).translated(option.rect.topLeft())

    def editorEvent(self, event, model, option, index):
        # https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/itemviews/qstyleditemdelegate.cpp?h=5.13#n278
        flags = model.flags(index)
        if (
            not (flags & QtCore.Qt.ItemIsUserCheckable)
            or not (option.state & QtWidgets.QStyle.State_Enabled)
            or not (flags & QtCore.Qt.ItemIsEnabled)
        ):
            return False
        value = index.data(QtCore.Qt.CheckStateRole)
        if value is None:
            return False
        widget = option.widget
        style = widget.style() if widget is not None else QtWidgets.QApplication.style()
        if event.type() in (
            QtCore.QEvent.MouseButtonRelease,
            QtCore.QEvent.MouseButtonDblClick,
            QtCore.QEvent.MouseButtonPress,
        ):
            viewOpt = QtWidgets.QStyleOptionViewItem(option)
            self.initStyleOption(viewOpt, index)
            checkRect = self.checkRect(viewOpt)
            if event.button() != QtCore.Qt.LeftButton or not checkRect.contains(
                event.pos()
            ):
                return False
            if event.type() in (
                QtCore.QEvent.MouseButtonPress,
                QtCore.QEvent.MouseButtonDblClick,
            ):
                return True
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.key() not in (QtCore.Qt.Key_Space, QtCore.Qt.Key_Select):
                return False
        else:
            return False
        state = value
        if flags & QtCore.Qt.ItemIsUserTristate:
            state = QtCore.Qt.CheckState((state + 1) % 3)

        else:
            state = (
                QtCore.Qt.Unchecked if state == QtCore.Qt.Checked else QtCore.Qt.Checked
            )
        return model.setData(index, state, QtCore.Qt.CheckStateRole)