Опираясь на ответ @Bruno, я использую стандартную QSortFilterProxyModel
функцию setFilterRegExp
для изменения строки поиска. Таким образом, подклассификация не требуется.
Это также исправляет ошибку в ответе @ Bruno, из-за которой предложения по некоторым причинам исчезли после того, как во время ввода введенная строка была исправлена с помощью backspace.
class CustomQCompleter(QtGui.QCompleter):
"""
adapted from: http://stackoverflow.com/a/7767999/2156909
"""
def __init__(self, *args):#parent=None):
super(CustomQCompleter, self).__init__(*args)
self.local_completion_prefix = ""
self.source_model = None
self.filterProxyModel = QtGui.QSortFilterProxyModel(self)
self.usingOriginalModel = False
def setModel(self, model):
self.source_model = model
self.filterProxyModel = QtGui.QSortFilterProxyModel(self)
self.filterProxyModel.setSourceModel(self.source_model)
super(CustomQCompleter, self).setModel(self.filterProxyModel)
self.usingOriginalModel = True
def updateModel(self):
if not self.usingOriginalModel:
self.filterProxyModel.setSourceModel(self.source_model)
pattern = QtCore.QRegExp(self.local_completion_prefix,
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.FixedString)
self.filterProxyModel.setFilterRegExp(pattern)
def splitPath(self, path):
self.local_completion_prefix = path
self.updateModel()
if self.filterProxyModel.rowCount() == 0:
self.usingOriginalModel = False
self.filterProxyModel.setSourceModel(QtGui.QStringListModel([path]))
return [path]
return []
class AutoCompleteComboBox(QtGui.QComboBox):
def __init__(self, *args, **kwargs):
super(AutoCompleteComboBox, self).__init__(*args, **kwargs)
self.setEditable(True)
self.setInsertPolicy(self.NoInsert)
self.comp = CustomQCompleter(self)
self.comp.setCompletionMode(QtGui.QCompleter.PopupCompletion)
self.setCompleter(self.comp)#
self.setModel(["Lola", "Lila", "Cola", 'Lothian'])
def setModel(self, strList):
self.clear()
self.insertItems(0, strList)
self.comp.setModel(self.model())
def focusInEvent(self, event):
self.clearEditText()
super(AutoCompleteComboBox, self).focusInEvent(event)
def keyPressEvent(self, event):
key = event.key()
if key == 16777220:
# Enter (if event.key() == QtCore.Qt.Key_Enter) does not work
# for some reason
# make sure that the completer does not set the
# currentText of the combobox to "" when pressing enter
text = self.currentText()
self.setCompleter(None)
self.setEditText(text)
self.setCompleter(self.comp)
return super(AutoCompleteComboBox, self).keyPressEvent(event)
Обновление:
Я полагал, что мое предыдущее решение работало, пока строка в выпадающем списке не соответствовала ни одному из элементов списка. Затем QFilterProxyModel
был пуст, и это, в свою очередь, сбросило text
в выпадающем списке. Я пытался найти элегантное решение этой проблемы, но сталкивался с проблемами (ссылаясь на ошибки удаленных объектов) всякий раз, когда пытался что-то изменить в self.filterProxyModel
. Так что теперь взломать - установить модель self.filterProxyModel
каждый раз, когда обновляется ее шаблон. И всякий раз, когда шаблон больше не совпадает с чем-либо в модели, чтобы дать ему новую модель, которая просто содержит текущий текст (он же path
в splitPath
). Это может привести к проблемам с производительностью, если вы имеете дело с очень большими моделями, но для меня хак работает довольно хорошо.
Обновление 2:
Я понял, что это все еще не идеальный путь, потому что, если новая строка вводится в поле со списком и пользователь нажимает клавишу ввода, поле со списком снова очищается. Единственный способ ввести новую строку - это выбрать ее из выпадающего меню после ввода.
Обновление 3:
Теперь ввод работает также. Я работал над сбросом текста в выпадающем списке, просто снимая его, когда пользователь нажимает ввод. Но я вернул его обратно, чтобы функциональность завершения осталась на месте. Если пользователь решает продолжить редактирование.