QCompleter выбрать данные из нескольких столбцов - PullRequest
0 голосов
/ 12 июня 2018

Я пытаюсь завершить заполнение строки при редактировании строки, когда в нее вводится некоторая строка типа «имя, фамилия», каждая из которых соответствует одному из двух столбцов модели таблицы (конечная цель состоит в том, чтобыбыть гибким, но на данный момент я просто хочу, чтобы это работало).В настоящее время я пытаюсь сделать следующее:

a) объединить результаты из двух столбцов в прокси и третий столбец, который просто имеет имя, фамилию как дискретную строку (что неудобно, еслипользователь вводит его как фамилию, имя. Текущая реализация не работает, потому что когда я пытаюсь сделать setCompletionColumn для столбца «поддельные», он не активирует мой метод переопределенных данных. columnCount включает в себя поддельный столбец, хотя)

б) иметь Completer, который не фильтрует, с прокси-моделью бэкэнда, фильтр acceptptsrow () которого фильтрует как первый, так и последний (не знаю, как сделать нефильтрующий завершитель - иначе он просто просматривает один столбец ивсплывающее окно в конце показывает группу имен без фамилий)

в) подделать модель дерева и сделать так, чтобы, когда я ставлю запятую, модель просматривает "ветку", состоящую из всехлюди, чьи имена ИЛИ (начинаются с?) - строка перед запятой.(Я просто не уверен, как начать с этого, он использует тот же тип acceptptsrow, что и b), но я не знаю, как делать это как отдельные ~ ветви ~)

это вызывает у меня головную боль, ини одна из этих реализаций не особенно приятна.Я делаю это слишком тяжело для себя и люблю использовать неправильный инструмент, или это просто то, к чему я должен пристегнуться?Я могу вставить некоторый код, но мне интересно, если то, что я делаю, имеет смысл.


обновление: фрагменты кода и пиксель

![enter image description here] 1

красный круг - это то, о чем я говорю с несколькими именами, они соответствуют различным записям в таблице, но завершитель ограничен одним столбцом.С точки зрения щелчка и получения имени пользователя, которое я понял, это в основном понятно, это просто формат и обработка запятых.

Вот часть моего полного класса:

 class CustomQCompleter(QCompleter): #dropdown box of suggestions    
    def __init__(self, parent=None,*args):#parent=None):
        self.columnind=0
        super(CustomQCompleter, self).__init__(*args)
        self.parent=parent
        self.setCompletionColumn(self.columnind)
    def pathFromIndex(self,index,role=QtCore.Qt.DisplayRole): #decides what gets put into selection box
        #print(index)
        model=self.source_model
        return model.data(model.index(index.row(),self.columnind),role=QtCore.Qt.DisplayRole)
    def splitPath(self, path):
        self.local_completion_prefix = path #???
        print(path)
        sp=self.local_completion_prefix.split(',')
        if len(sp)>1: #
            print('path split')
            return sp

        return [path]

и вот акцепт на моей повторно реализованной прокси-модели:

def filterAcceptsRow(self, row_num, parent): #input matches if this returns true       
    self.filterString=self.parent().inp.text()
    self.filterList=[i.strip() for i in self.filterString.split(',')]
    l=len(self.filterList)
    while l<2:#easiest way to get thru this atm, clean up later
        self.filterList.append('')
        l=len(self.filterList)
    if self.baseModel is not None:
        model=self.baseModel
    else:
        model = self.sourceModel()  # the underlying model, 

                      # implmented as a python array

    row = [model.data(model.index(row_num,0)),model.data(model.index(row_num,1))] #gets the data for this row from sql model in list format



    #(row[0] starts with fname and row[1] starts w lname) OR vice versa
    tests=[row[0].startswith(self.filterList[i]) and row[1].startswith(self.filterList[1-i]) for i in [0,1]]
    #tests = len(self.filterSet.intersection(set([row[col] for col in self.filterColumns])))==2
   # print(tests)
    return True in tests   

и в идеале это будет выглядеть примерно так:

enter image description here

1 Ответ

0 голосов
/ 13 июня 2018

Стратегия состоит в том, чтобы использовать прокси-сервер, в котором создается новая роль, которая будет возвращать объединенные тексты, поэтому для отображения всплывающего окна с объединенными текстами мы создадим пользовательский делегат для popup() из QCompleter.

import sys

from PyQt5.QtCore import QIdentityProxyModel, Qt
from PyQt5.QtWidgets import QStyledItemDelegate, QCompleter, QApplication, QWidget, \
    QVBoxLayout, QLineEdit, QTableView, QStyleOptionViewItem, QStyle
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel

JoinRole = Qt.UserRole +1

class JoinProxyModel(QIdentityProxyModel):
    def __init__(self, columns, *args, **kwargs):
        QIdentityProxyModel.__init__(self, *args, **kwargs)
        self._columns = columns

    def data(self, index, role):
        if role == JoinRole:
            texts = []
            for c in self._columns:
                texts.append(self.sibling(index.row(), c, index.parent()).data())
            return ", ".join(texts)
        return QIdentityProxyModel.data(self, index, role)

class JoinDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        opt = QStyleOptionViewItem(option)
        self.initStyleOption(opt, index)
        opt.text = index.data(JoinRole)
        widget = option.widget
        style = widget.style() if widget else QApplication.style()
        style.drawControl(QStyle.CE_ItemViewItem, opt, painter, widget)


class JoinCompleter(QCompleter):
    def __init__(self, model, columns, parent=None):
        QCompleter.__init__(self, parent)
        # columns: are the columns that are going to concatenate
        proxy = JoinProxyModel(columns)
        proxy.setSourceModel(model)
        self.setModel(proxy)

        self.setCompletionRole(JoinRole)
        self.setFilterMode(Qt.MatchContains)
        self.popup().setItemDelegate(JoinDelegate(self))


def createConnection():
    db = QSqlDatabase.addDatabase("QSQLITE");
    db.setDatabaseName(":memory:")
    if not db.open():
        QMessageBox.critical(nullptr, QObject.tr("Cannot open database"),
            QObject.tr("Unable to establish a database connection.\n"
                        "This example needs SQLite support. Please read "
                        "the Qt SQL driver documentation for information how "
                        "to build it.\n\n"
                        "Click Cancel to exit."), QMessageBox.Cancel)
        return False

    query = QSqlQuery()
    query.exec_("create table person (id int primary key, "
               "firstname varchar(20), lastname varchar(20))")
    query.exec_("insert into person values(101, 'Danny', 'Young')")
    query.exec_("insert into person values(102, 'Christine', 'Holand')")
    query.exec_("insert into person values(103, 'Lars', 'Gordon')")
    query.exec_("insert into person values(104, 'Roberto', 'Robitaille')")
    query.exec_("insert into person values(105, 'Maria', 'Papadopoulos')")

    return True

if __name__ == '__main__':
    app = QApplication(sys.argv)

    if not createConnection():
        sys.exit(-1)

    w = QWidget()

    lay = QVBoxLayout(w)
    le = QLineEdit()
    view = QTableView()
    model = QSqlTableModel()
    model.setTable("person")
    model.select()

    completer = JoinCompleter(model, [1, 2])
    le.setCompleter(completer)
    view.setModel(model)

    lay.addWidget(le)
    lay.addWidget(view)

    w.show()
    sys.exit(app.exec_())
...