简体   繁体   中英

How to get a Custom QCompleter to work with a custom item delegate?

I have a custom qcompleter (to match any part of the string) and a custom QStyledItemDelegate (to show different formatting on the drop down options returned by the qcompleter) applied to a QLineEdit, and they both work individually however the QStyledItemDelegate doesn't work when I apply them both.

import sys
from PySide2.QtWidgets import QApplication, QMainWindow, QLineEdit, QCompleter, QStyledItemDelegate
from PySide2.QtCore import Qt, QSortFilterProxyModel, QStringListModel
from PySide2.QtGui import QColor, QPalette

Qcompleter Item delegate:

class CompleterItemDelegate(QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(CompleterItemDelegate, self).initStyleOption(option, index)
        option.backgroundBrush = QColor("red")
        option.palette.setBrush(QPalette.Text, QColor("blue"))
        option.displayAlignment = Qt.AlignCenter

Custom QCompleter:

class CustomQCompleter(QCompleter):
    def __init__(self, parent=None):
        super(CustomQCompleter, self).__init__(parent)
        self.local_completion_prefix = ""
        self.source_model = None

    def setModel(self, model):
        self.source_model = model
        super(CustomQCompleter, self).setModel(self.source_model)

    def updateModel(self):
        local_completion_prefix = self.local_completion_prefix
        class InnerProxyModel(QSortFilterProxyModel):
            def filterAcceptsRow(self, sourceRow, sourceParent):
                index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
                searchStr = local_completion_prefix.lower()
                searchStr_list = searchStr.split()
                modelStr = self.sourceModel().data(index0,Qt.DisplayRole).lower()
                for string in searchStr_list:
                    if not string in modelStr:
                        return False
                return True

        proxy_model = InnerProxyModel()
        proxy_model.setSourceModel(self.source_model)
        super(CustomQCompleter, self).setModel(proxy_model)

    def splitPath(self, path):
        self.local_completion_prefix = str(path)
        self.updateModel()
        return ""

Main:

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        model = QStringListModel()
        model.setStringList(['Tom', 'Tommy Stevens', 'Steven'])

        # ITEM DELEGATE ONLY - WORKS
        # completer = QCompleter()
        # completer.setModel(model)
        # delegate = CompleterDelegate()
        # completer.popup().setItemDelegate(delegate)

        # QCOMPLETER DELEGATE ONLY - WORKS
        # completer = CustomQCompleter(self)
        # completer.setModel(model)

        # ITEM DELEGATE AND QCOMPLETER DELEGATE - ITEM DELEGATE DOESNT WORK
        completer = CustomQCompleter(self)
        completer.setModel(model)
        delegate = CompleterItemDelegate()
        completer.popup().setItemDelegate(delegate)

        self.lineEdit = QLineEdit()
        self.lineEdit.setCompleter(completer)

        self.setCentralWidget(self.lineEdit)
        self.show()

if __name__ == '__main__':
    app  = QApplication(sys.argv)
    p = MainWindow()
    p.show()
    sys.exit(app.exec_())
  1. Is there a way to make this code work?
  2. Is there a better way to achieve both choosing the completion rules for the qcompleter and formatting the popup results?

Setting the delegate on the popup won't work if the model is set afterwards, since setModel() also calls setPopup() , which in turn sets a new item delegate .

So, you either:

  • ensure that you set the delegate after setting the model on the completer;
  • subclass the completer and override setModel() , by calling the base implementation and then restore the delegate, or complete() by restoring the delegate before the base implementation call; note that this won't work in your case because you called the base implementation in updateModel() which will clearly ignore the override;

Moving

delegate = CompleterItemDelegate()
self.popup().setItemDelegate(delegate)

into the CustomQCompleter updateModel function solves the problem as pointed out by musicamante.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM