Search code examples
pythonpyqtpyqt4

Cannot select item from QListView with custom QStyledItemDelegate


I want to render each row with HTML code. The rendering works but -at least for me- items cannot be selected individually.

import sys
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

#################################################################### 
def main(): 
    app = QApplication(sys.argv) 
    w = MyWindow() 
    w.show() 
    sys.exit(app.exec_()) 


list_data = [1,2,3,4]

#################################################################### 
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args) 

        # create table        
        lm = MyListModel(list_data, self)
        lv = QListView()
        lv.setModel(lm)
        lv.setItemDelegate(HTMLDelegate(self))

        # layout
        layout = QVBoxLayout()
        layout.addWidget(lv) 
        self.setLayout(layout)

#################################################################### 
class MyListModel(QAbstractListModel): 
    def __init__(self, datain, parent=None, *args): 
        """ datain: a list where each item is a row
        """
        QAbstractListModel.__init__(self, parent, *args) 
        self.listdata = datain

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

    def data(self, index, role): 
        if index.isValid() and role == Qt.DisplayRole:
            return QVariant(self.listdata[index.row()])
        else: 
            return QVariant()

class HTMLDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        painter.save()

        model = index.model()
        record = model.listdata[index.row()]
        doc = QTextDocument(self)
        doc.setHtml("<b>%s</b>"%record)
        doc.setTextWidth(option.rect.width())
        ctx = QAbstractTextDocumentLayout.PaintContext()

        painter.translate(option.rect.topLeft());
        painter.setClipRect(option.rect.translated(-option.rect.topLeft()))
        dl = doc.documentLayout()
        dl.draw(painter, ctx)
        painter.restore()


    def sizeHint(self, option, index):
        model = index.model()
        record = model.listdata[index.row()]
        doc = QTextDocument(self)
        doc.setHtml("<b>%s</b>"%record)
        doc.setTextWidth(option.rect.width())
        return QSize(doc.idealWidth(), doc.size().height())
    def flags(self, index):
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable 
####################################################################
if __name__ == "__main__": 
    main()

Solution

  • I believe what you should do is detect selected items in your HTMLDelegate.paint method and fill the background with "highlight" color if such items detected. I've slightly changed your paint method, pls, check if it works for you

    def paint(self, painter, option, index):
        painter.save()
    
        # highlight selected items
        if option.state & QtGui.QStyle.State_Selected:  
            painter.fillRect(option.rect, option.palette.highlight());
    
        model = index.model()
        record = model.listdata[index.row()]
        doc = QTextDocument(self)
        doc.setHtml("<b>%s</b>"%record)
        doc.setTextWidth(option.rect.width())
        ctx = QAbstractTextDocumentLayout.PaintContext()
    
        painter.translate(option.rect.topLeft());
        painter.setClipRect(option.rect.translated(-option.rect.topLeft()))
        dl = doc.documentLayout()
        dl.draw(painter, ctx)
    
        painter.restore()
    

    hope this helps, regards