Yes, I have mouse tracking enabled.
Maybe my description of the issue was not very clear, sorry. When I click for the first time on the button, it does not change its style to QStyle.State_Sunken and then back to QStyle.State_Raised on release, this happens only on the every second click. Also button does not change its style on mouse hover.
Here is a minimal example
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MyDelegate(QStyledItemDelegate):
def __init__(self):
super(MyDelegate, self).__init__()
self.buttonRect = None
def sizeHint(self, option, index):
fm = QFontMetrics(option.font)
return QSize(150, fm.height() * 5 + fm.leading())
def paint(self, painter, option, index):
name = index.data(Qt.DisplayRole)
description = index.data(Qt.UserRole + 1)
btnStyle = QStyle.State(index.data(Qt.UserRole + 2))
nameFont = QFont(option.font)
nameFont.setWeight(QFont.Weight.Bold)
fm = QFontMetrics(nameFont)
padding = fm.lineSpacing() // 2
nameRect = QRect(option.rect)
nameRect.setLeft(nameRect.left() + padding)
nameRect.setTop(nameRect.top() + padding)
nameRect.setRight(nameRect.right() - padding)
nameRect.setHeight(fm.lineSpacing())
descrRect = QRect(option.rect)
descrRect.setLeft(descrRect.left() + padding)
descrRect.setTop(nameRect.bottom())
descrRect.setRight(descrRect.right() - padding)
descrRect.setHeight(fm.lineSpacing())
btnText = "Add"
textWidth = fm.width(btnText)
self.btnRect = QRect(option.rect)
self.btnRect.setLeft(descrRect.right() - textWidth * 2)
self.btnRect.setTop(descrRect.bottom() + padding)
self.btnRect.setRight(descrRect.right() - padding)
self.btnRect.setHeight(fm.lineSpacing() * 2)
borderRect = QRect(option.rect.marginsRemoved(QMargins(4, 4, 4, 4)))
painter.save()
pen = painter.pen()
if option.state & QStyle.State_MouseOver:
pp = QPen(option.palette.highlight().color())
pp.setWidth(2)
painter.setPen(pp)
painter.drawRect(borderRect)
painter.setPen(pen)
painter.setFont(nameFont)
elided_text = fm.elidedText(name, Qt.ElideRight, nameRect.width())
painter.drawText(nameRect, Qt.AlignLeading, elided_text)
painter.setFont(option.font)
fm = QFontMetrics(QFont(option.font))
elided_text = fm.elidedText(description, Qt.ElideRight, descrRect.width())
painter.drawText(descrRect, Qt.AlignLeading, elided_text)
button = QStyleOptionButton()
button.text = btnText
button.state = btnStyle
button.rect = self.btnRect
QApplication.style().drawControl(QStyle.CE_PushButton, button, painter)
painter.restore()
def editorEvent(self, event, model, option, index):
if self.btnRect.contains(event.pos()):
if event.type() == QEvent.MouseButtonPress:
model.setData(index, QStyle.State_Sunken, Qt.UserRole + 2)
elif event.type() == QEvent.MouseButtonRelease:
model.setData(index, QStyle.State_Raised, Qt.UserRole + 2)
self.do_something()
else:
model.setData(index, QStyle.State_Enabled | QStyle.State_HasFocus, Qt.UserRole + 2)
else:
model.setData(index, QStyle.State_Enabled, Qt.UserRole + 2)
return super(MyDelegate, self).editorEvent(event, model, option, index)
def do_something(self):
print("button clicked")
if __name__ == "__main__":
app = QApplication(sys.argv)
model = QStandardItemModel()
for i in range(10):
item = QStandardItem()
item.setData(f"Item {i}", Qt.DisplayRole)
item.setData("Item description, can be very long", Qt.UserRole + 1)
item.setData(QStyle.State_Enabled, Qt.UserRole + 2)
model.appendRow(item)
listView = QListView()
listView.setMouseTracking(True)
listView.setItemDelegate(MyDelegate())
listView.setModel(model)
listView.show()
app.exec()