Как да накарам подклас на QStyledItemDelegate да реагира правилно при задържане на мишката в QListView в PySide/PyQt?

На път да реша проблемите, които посочих в предишни въпроси (въпрос 1, въпрос 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 числа, които получих от простото отпечатване на int(option.state). Както и да е: не работи много добре! Долната граница на рамката не променя цвета си и понякога се случват странни неща.

Може ли някой да ми покаже по-добър начин да направя това? Оптимално е използването на цветове от QStyle за промяна на контура и цвета на фона, така че да може да се персонализира с помощта на StyleSheet?

Всички съвети или обяснения са високо оценени.


person physicsPyUser    schedule 27.10.2015    source източник


Отговори (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, за да научите защо флаговете се комбинират и извличат по този начин и вие получавате резултати, които са наистина големи цели числа, когато ги отпечатате.

И накрая, подозирам, че долната граница не променя цвета си, защото следващият елемент отдолу рисува границата върху нея. Това вероятно означава, че изчислението на размера на правоъгълника е грешно. Предлагам да направите известно отстраняване на грешки по този начин, за да видите дали можете да разберете защо.

person three_pineapples    schedule 27.10.2015