如何使 QStyledItemDelegate 的子类在 PySide/PyQt 的 QListView 中的鼠标悬停时正确反应? [英] How to make subclass of QStyledItemDelegate react properly on mouse hover in a QListView in PySide/PyQt?

查看:91
本文介绍了如何使 QStyledItemDelegate 的子类在 PySide/PyQt 的 QListView 中的鼠标悬停时正确反应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我解决之前问题中提到的问题的路上(问题 1问题 2) 单独,我成功地实现了一个自定义 QStyledItemDelegate 满足我的要求.下面是一个说明当前状态的最小工作示例:

On my way to solve the problems I stated in earlier questions (question 1, question 2) alone, I succeeded to implement a custom QStyledItemDelegate which meets my demands. Here is a minimal working example illustrating the current state:

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' 使我能够检测点击和双击.

I used a dummy class DataRef which returns the dummy labels and pixmap for the delegate. The delegate is simply a rectangular outline with a pixmap at the left and 2 lines of formatted text at the right. The 'editorEvent' enables me to detect clicks and double-clicks.

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

The MyListDelegate.paint() function receives option.state values which seem strange to me. They do not correspond to a QStyle.State which I know. So I'm now using this large int numbers which I got from simply printing int(option.state). Anyway: it doesn't work quite well! The lower border of the frame does not change it's color and strange things happen sometimes.

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

Can anyone show me a better way to do that? Optimally, using colors from QStyle for changing the outline and background color, so that it is customizable using the StyleSheet?

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

Any hints or explanations are highly appreciated.

推荐答案

option.state 的值是多个QStyle.state 标志.

The value of option.state is the result of a bitwise or operation of many QStyle.state flags.

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

To see if you currently need to draw the mouseOver state, you just need to do:

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

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

There may be many other flags set, which you can choose to handle or not by writing similar if statements (all the flags are listed in the c++ documentation I linked to above).

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

I suspect this is why you are seeing inconsistent behaviour. You are only catching a couple of the mouseOver state flags and ignoring times when other (unrelated) style flags are also set. I suggest reading up about bitwise and-ing and or-ing to learn why flags are combined and extracted like this, and you you get results that are really large integers when you print it.

最后,我怀疑下边框没有改变颜色,因为下面的下一项是在它上面绘制边框.这可能意味着您对矩形大小的计算是错误的.我建议沿着这条线做一些调试,看看你是否能找出原因.

Finally, I suspect that the lower border is not changing colour because the next item below is drawing the border on top of it. This probably means you calculation for the size of the rectangle is wrong. I suggest doing some debugging along this lines to see if you can work out why.

这篇关于如何使 QStyledItemDelegate 的子类在 PySide/PyQt 的 QListView 中的鼠标悬停时正确反应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆