【问题标题】:How to make subclass of QStyledItemDelegate react properly on mouse hover in a QListView in PySide/PyQt?如何使 QStyledItemDelegate 的子类在 PySide/PyQt 的 QListView 中鼠标悬停时正确反应?
【发布时间】:2016-01-27 01:27:08
【问题描述】:

在解决我在前面的问题(question 1question 2)中提出的问题的过程中,我成功地实现了一个满足我需求的自定义QStyledItemDelegate。这是一个说明当前状态的最小工作示例

import sys
import PySide.QtCore as core
import PySide.QtGui as gui


class DataRef(object):

    def __init__(self, i):
        self.i = i

    def upperLabel(self):
        return u'upperLabel {0}'.format(self.i)

    def lowerLabel(self):
        return u'lowerLabel {0}'.format(self.i)

    def pixmap(self):
        return gui.QPixmap(90, 90)

class MyListModel(core.QAbstractListModel):

    def __init__(self, parent=None):
        super(MyListModel, self).__init__(parent)
        self._items = [DataRef(i) for i in range(20)]

    def rowCount(self, parent=core.QModelIndex()):
        return len(self._items)

    def data(self, index, role=core.Qt.DisplayRole):

        if not index.isValid():
            return None

        if role == core.Qt.DisplayRole:
            return self._items[index.row()]
        return

class MyListDelegate(gui.QStyledItemDelegate):

    w = 300
    imSize = 90
    pad = 5
    h = imSize + 2*pad
    sepX = 10

    def __init__(self, parent=None):
        super(MyListDelegate, self).__init__(parent)

    def paint(self, painter, option, index):
        mouseOver = option.state in [73985, 73729]

        if option.state & gui.QStyle.State_Selected:
            painter.fillRect(option.rect, painter.brush())

        pen = painter.pen()
        painter.save()

        x,y = (option.rect.x(), option.rect.y())
        dataRef = index.data()
        pixmap = dataRef.pixmap()
        upperLabel = dataRef.upperLabel()
        lowerLabel = dataRef.lowerLabel()

        if mouseOver:
            newPen = gui.QPen(core.Qt.green, 1, core.Qt.SolidLine)
            painter.setPen(newPen)
        else:
            painter.setPen(pen)
        painter.drawRect(x, y, self.w, self.h)
        painter.setPen(pen)

        x += self.pad
        y += self.pad

        painter.drawPixmap(x, y, pixmap)

        font = painter.font()
        textHeight  = gui.QFontMetrics(font).height()

        sX = self.imSize + self.sepX
        sY = textHeight/2

        font.setBold(True)
        painter.setFont(font)
        painter.drawText(x+sX, y-sY, 
                         self.w-self.imSize-self.sepX, self.imSize,
                         core.Qt.AlignVCenter,
                         upperLabel)
        font.setBold(False)
        font.setItalic(True)
        painter.setFont(font)
        painter.drawText(x+sX, y+sY,
                         self.w-self.imSize-self.sepX, self.imSize,
                         core.Qt.AlignVCenter,
                         lowerLabel)

        painter.restore()

    def sizeHint(self, option, index):
        return core.QSize(self.w, self.imSize+2*self.pad)

    def editorEvent(self, event, model, option, index):
        if event.type() == core.QEvent.MouseButtonRelease:
            print 'Clicked on Item', index.row()
        if event.type() == core.QEvent.MouseButtonDblClick:
            print 'Double-Clicked on Item', index.row()
        return True

if __name__ == '__main__':


    app = gui.QApplication(sys.argv)
    app.setStyleSheet('QListView::item:hover {background: none;}')
    mw = gui.QMainWindow()

    model = MyListModel()
    view = gui.QListView()
    view.setItemDelegate(MyListDelegate(parent=view))
    view.setSpacing(5)
    view.setModel(model)

    mw.setCentralWidget(view)
    mw.show()

    sys.exit(app.exec_())

我使用了一个虚拟类DataRef,它返回代表的虚拟标签和像素图。代表只是一个矩形轮廓,左侧有像素图,右侧有 2 行格式化文本。 'editorEvent' 使我能够检测点击和双击。

问题

MyListDelegate.paint() 函数接收 option.state 值,这对我来说似乎很奇怪。它们与我知道的QStyle.State 不对应。所以我现在使用这个大整数,我通过简单地打印int(option.state) 得到。无论如何:它不太好用!框架的下边框不会改变它的颜色,有时会发生奇怪的事情。

谁能告诉我一个更好的方法来做到这一点?最好使用 QStyle 中的颜色来更改轮廓和背景颜色,以便使用 StyleSheet 对其进行自定义?

非常感谢任何提示或解释。

【问题讨论】:

    标签: python-2.7 pyqt pyside


    【解决方案1】:

    option.state 的值是多个QStyle.state 标志位或运算的结果。

    要查看您当前是否需要绘制mouseOver 状态,您只需要这样做:

    if option.state & QStyle.State_MouseOver:
        # draw mouseover stuff
    else:
        # don't draw mouse over stuff
    

    可能还有许多其他标志集,您可以通过编写类似的 if 语句来选择处理或不处理(所有标志都列在我上面链接的 c++ 文档中)。

    我怀疑这就是您看到不一致行为的原因。当其他(不相关的)样式标志也被设置时,您只会捕获几个 mouseOver 状态标志并忽略时间。我建议阅读有关按位 and-ing 和 or-ing 的内容,以了解为什么像这样组合和提取标志,并且在打印时得到的结果是非常大的整数。

    最后,我怀疑下边框没有改变颜色,因为下面的下一项是在它上面绘制边框。这可能意味着您对矩形大小的计算是错误的。我建议您按照这些思路进行一些调试,看看您是否可以找出原因。

    【讨论】:

    • 感谢您的出色回答。我完全不知道这种编程,现在它工作得很好。我还设法通过editorEvent 中的index.model() 方法为Clicked 和DblClicked 发送适当的信号。无论如何,我没有想到的最后一件事是保持选中一个项目。我在视图中激活了各种选择相关的参数,我也在模型的flags-方法中返回了core.Qt.ItemIsSelectable。但是option.state & gui.QStyle.State_Selected 仍然没有被填满。你对最后一个问题有什么提示吗?
    • @physicsPyUser 很高兴我能帮上忙!您的下一个问题可能很复杂,可以提出关于 SO 的新问题。这样可以提供详细的答案
    • 好的,找到后续问题here。如果你能再看一遍,我会很高兴。
    猜你喜欢
    • 2016-01-30
    • 2020-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-14
    • 1970-01-01
    相关资源
    最近更新 更多