Pyqt Auto Completion In A Table
Solution 1:
QCompleter
can be established a model that uses as a source for the autocomplete, we could pass the QModelIndex
model that provides the method createEditor(self, parent, option, index)
through index.model()
but the problem is that you can only take a column and is not what is desired.
Then we must do a conversion of the tablemodel to a listmodel, and then we must filter the repeated elements so that the completer shows unique elements, one way to do them is through proxies, classes that inherit from QAbstractProxyModel
, the scheme is as follows:
TableModel ----> ReadTable2ListProxyModel ----> DuplicateFilterProxyModel
En la siguiente sección muestros las clases:
classReadTable2ListProxyModel(QIdentityProxyModel):
defcolumnCount(self, parent=QModelIndex()):
return1defrowCount(self, parent=QModelIndex()):
return self.sourceModel().rowCount() * self.sourceModel().columnCount()
defmapFromSource(self, sourceIndex):
if sourceIndex.isValid() and sourceIndex.column() == 0\
and sourceIndex.row() < self.rowCount():
r = sourceIndex.row()
c = sourceIndex.column()
row = sourceIndex.model().columnCount() * c + r
return self.index(row, 0)
return QModelIndex()
defmapToSource(self, proxyIndex):
r = proxyIndex.row() / self.sourceModel().columnCount()
c = proxyIndex.row() % self.sourceModel().columnCount()
return self.sourceModel().index(r, c)
defindex(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column)
classDuplicateFilterProxyModel(QSortFilterProxyModel):
defsetSourceModel(self, model):
model.dataChanged.connect(lambda: self.invalidate())
QSortFilterProxyModel.setSourceModel(self, model)
deffilterAcceptsRow(self, row, parent):
value = self.sourceModel().index(row, self.filterKeyColumn())\
.data(self.filterRole())
if value isNone:
returnFalseif row == 0:
returnTruefor i inreversed(range(0, row)):
val = self.sourceModel().index(i, self.filterKeyColumn())\
.data(self.filterRole())
if val == value:
returnFalsereturnTrue
Then the conversion is established in the delegate:
class TableItemCompleter(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
completer = QCompleter(parent)
proxy1 = ReadTable2ListProxyModel(parent)
proxy2 = DuplicateFilterProxyModel(parent)
proxy1.setSourceModel(index.model())
proxy2.setSourceModel(proxy1)
completer.setModel(proxy2)
editor.setCompleter(completer)
return editor
In the following link you will find an example, and in the following image the operation is illustrated, in the first widget the table is observed, in the second the conversion to list and in the third the list eliminating duplicate elements.
If you want each item to have a list what can be done is to store the list in each item through a role that is not in use as Qt.UserRole
through the setData()
method, and in the delegate through the method data()
of the QModelIndex
:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import random
class TableItemCompleter(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
completion_ls = index.data(Qt.UserRole) # get list
completer = QCompleter(completion_ls, parent)
editor.setCompleter(completer)
return editor
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
lay = QHBoxLayout(self)
tv = QTableWidget(3, 4, self)
lay.addWidget(tv)
l = ["AA", "AB", "AC", "AD", "BA", "BB", "BC"]
for i in range(tv.rowCount()):
for j in range(tv.columnCount()):
it = QTableWidgetItem(f"{i},{j}")
tv.setItem(i, j, it)
it.setData(Qt.UserRole, random.sample(l, 3)) # set list
tv.setItemDelegate(TableItemCompleter(tv))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Post a Comment for "Pyqt Auto Completion In A Table"