pyqt auto completion in a table

自闭症网瘾萝莉.ら 提交于 2019-12-24 15:57:08

问题


I need auto completion in a table. So far, I could make it work that I get the same list for the entire table.

However, I need a dynamic list for each cell. How can I get update the list when I move to a new position in the cell?

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys


class mainWindow(QMainWindow):

    def __init__(self, parent = None):
        super(mainWindow, self).__init__(parent)

        self.initUI()


    def initUI(self):
        self.center_window = centerWindow(parent=self)
        self.setCentralWidget(self.center_window)


class centerWindow(QWidget):
    def __init__(self, parent=None):
        super(centerWindow, self).__init__(parent)

        table = QTableWidget()
        table.setItemDelegate(TableItemCompleter())
        table.setRowCount(5)
        table.setColumnCount(1)

        vbox = QVBoxLayout(self)
        vbox.addWidget(table)
        self.setLayout(vbox)


class TableItemCompleter(QStyledItemDelegate):
    def __init__(self, parent = None):
        super(TableItemCompleter, self).__init__(parent)

    def createEditor(self, parent, styleOption, index):
        editor = QLineEdit(parent)

        completion_ls = ['aaa', 'bbb', 'ccc']

        autoComplete = QCompleter(completion_ls)
        editor.setCompleter(autoComplete)
        return editor


if __name__ == '__main__':
    app = QApplication.instance()
    if app is None:
        app = QApplication(sys.argv)
    else:
        print('QApplication instance already exists: %s' % str(app))


    ex = mainWindow()
    ex.show()
    sys.exit(app.exec_())

To make it more clear. Instead of having the completion_ls list in the TableItemCompleter, I'd like to add something like this:

table.setItem(row, column) #add my new auto completion list

回答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:

class ReadTable2ListProxyModel(QIdentityProxyModel):
    def columnCount(self, parent=QModelIndex()):
        return 1

    def rowCount(self, parent=QModelIndex()):
        return self.sourceModel().rowCount() * self.sourceModel().columnCount()

    def mapFromSource(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()

    def mapToSource(self, proxyIndex):
        r = proxyIndex.row() / self.sourceModel().columnCount()
        c = proxyIndex.row() % self.sourceModel().columnCount()
        return self.sourceModel().index(r, c)

    def index(self, row, column, parent=QModelIndex()):
        return self.createIndex(row, column)


class DuplicateFilterProxyModel(QSortFilterProxyModel):
    def setSourceModel(self, model):
        model.dataChanged.connect(lambda: self.invalidate())
        QSortFilterProxyModel.setSourceModel(self, model)

    def filterAcceptsRow(self, row, parent):
        value = self.sourceModel().index(row, self.filterKeyColumn())\
            .data(self.filterRole())
        if value is None:
            return False
        if row == 0:
            return True
        for i in reversed(range(0, row)):
            val = self.sourceModel().index(i, self.filterKeyColumn())\
                .data(self.filterRole())
            if val == value:
                return False
        return True

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_())


来源:https://stackoverflow.com/questions/48123219/pyqt-auto-completion-in-a-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!