如何使 QStyledItemDelegate 的子类在 PySide/PyQt 中的 QListView 中鼠标悬停时做出正确反应?
How to make subclass of QStyledItemDelegate react properly on mouse hover in a QListView in PySide/PyQt?
在我独自解决前面问题(question 1, question 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_())
我使用了虚拟 class DataRef
,其中 returns 代表的虚拟标签和像素图。委托只是一个矩形轮廓,左侧有一个像素图,右侧有两行格式化文本。 'editorEvent' 使我能够检测点击和双击。
问题
MyListDelegate.paint()
函数接收 option.state
值,这对我来说很奇怪。它们不符合我所知道的 QStyle.State
。所以我现在使用的是我通过简单地打印 int(option.state)
得到的大整数。无论如何:它不能很好地工作!框架的下边框不会改变它的颜色,有时会发生奇怪的事情。
任何人都可以告诉我更好的方法吗?最好使用 QStyle 中的颜色来更改轮廓和背景颜色,以便使用 StyleSheet 进行自定义?
非常感谢任何提示或解释。
option.state
的值是许多 QStyle.state
标志位或操作的结果。
查看当前是否需要绘制mouseOver
状态,只需要做:
if option.state & QStyle.State_MouseOver:
# draw mouseover stuff
else:
# don't draw mouse over stuff
可能还有许多其他标志设置,您可以通过编写类似的 if 语句来选择是否处理这些标志(所有标志都列在我上面链接的 c++ 文档中)。
我怀疑这就是您看到不一致行为的原因。您只捕获了几个 mouseOver 状态标志并忽略了其他(不相关的)样式标志也被设置的时间。我建议阅读有关按位与运算和或运算的内容,以了解为什么要像这样组合和提取标志,并且在打印时会得到非常大的整数结果。
最后,我怀疑下边框没有改变颜色,因为下面的下一项是在它上面绘制边框。这可能意味着您对矩形大小的计算是错误的。我建议沿着这条线做一些调试,看看你是否能找出原因。
在我独自解决前面问题(question 1, question 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_())
我使用了虚拟 class DataRef
,其中 returns 代表的虚拟标签和像素图。委托只是一个矩形轮廓,左侧有一个像素图,右侧有两行格式化文本。 'editorEvent' 使我能够检测点击和双击。
问题
MyListDelegate.paint()
函数接收 option.state
值,这对我来说很奇怪。它们不符合我所知道的 QStyle.State
。所以我现在使用的是我通过简单地打印 int(option.state)
得到的大整数。无论如何:它不能很好地工作!框架的下边框不会改变它的颜色,有时会发生奇怪的事情。
任何人都可以告诉我更好的方法吗?最好使用 QStyle 中的颜色来更改轮廓和背景颜色,以便使用 StyleSheet 进行自定义?
非常感谢任何提示或解释。
option.state
的值是许多 QStyle.state
标志位或操作的结果。
查看当前是否需要绘制mouseOver
状态,只需要做:
if option.state & QStyle.State_MouseOver:
# draw mouseover stuff
else:
# don't draw mouse over stuff
可能还有许多其他标志设置,您可以通过编写类似的 if 语句来选择是否处理这些标志(所有标志都列在我上面链接的 c++ 文档中)。
我怀疑这就是您看到不一致行为的原因。您只捕获了几个 mouseOver 状态标志并忽略了其他(不相关的)样式标志也被设置的时间。我建议阅读有关按位与运算和或运算的内容,以了解为什么要像这样组合和提取标志,并且在打印时会得到非常大的整数结果。
最后,我怀疑下边框没有改变颜色,因为下面的下一项是在它上面绘制边框。这可能意味着您对矩形大小的计算是错误的。我建议沿着这条线做一些调试,看看你是否能找出原因。