I am making a GUI using Pyqt5 where there are 5 Qlineedit fields.
The range of values for each can be say [1 -5]. If the user tried to select third Qlineedit field and selected the value '2'. The other fields range should now not include '2' in it.
Similarly if he selected another field with value '4', the remaining fields should have only [1, 3, 5] as available options.
(Please bear in mind that user can delete the value in any field too and available values should be updated accordingly.)
I tried to use list and update QCompleter for the fields as soon as I see any textChanged signal.
The below code works perfectly except when the user lets say had given the input '14' in some field before and now tries to delete it using backspace. '4' will get deleted but on deleting '1' it will crash without any backtrace.
"Process finished with exit code -1073741819 (0xC0000005)"
If I click on the lineedit field before deleting '1', it works fine.
Minimal code -
#!/usr/bin/env python
import logging
import sys
import traceback
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QApplication, QCompleter
class MyWidget(QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
vbox = QVBoxLayout(self)
self.setLayout(vbox)
self.names = ['11', '12', '13', '14', '15']
self.names.sort()
self.line_edits_list = [0]*5
for i in range(len(self.names)):
self.line_edits_list[i] = QLineEdit(self)
completer = QCompleter(self.names, self.line_edits_list[i])
self.line_edits_list[i].setCompleter(completer)
vbox.addWidget(self.line_edits_list[i])
self.line_edits_list[i].textEdited.connect(self.text_changed)
def text_changed(self, text):
names_sel = []
# Check if current text matches anything in our list, if it does add it to a new list
for i in range(len(self.line_edits_list)):
if self.line_edits_list[i].text() in self.names and self.line_edits_list[i].text() not in names_sel:
names_sel.append(self.line_edits_list[i].text())
# The remaining textfields should get their qcompleter ranges updated with unique values of the two lists
for i in range(len(self.line_edits_list)):
if self.line_edits_list[i].text() not in self.names:
try:
new_range = list((set(self.names) - set(names_sel)))
completer = QCompleter(new_range, self.line_edits_list[i])
self.line_edits_list[i].setCompleter(completer)
except:
print(traceback.format_exc())
def test():
app = QApplication(sys.argv)
w = MyWidget()
w.show()
app.exec_()
print("END")
if __name__ == '__main__':
test()
The correct way to implement this is by using a model with the QCompleter. The model can change its content over time and the completer will react to it accordingly.
In your case, you could create a model that contains all possible values first. Then, you could use a
QSortFilterProxyModel, which, given you current UI state, could reduce the set. The proxy model is the one that you use withQCompleter.This (otherwise unrelated) question is example Python code for implementing such a proxy model: QSortFilterProxyModel does not apply Caseinsensitive