QCompleter выбрать данные из нескольких столбцов
Я пытаюсь завершить завершитель при редактировании строки, где вводится какая-то строка, такая как "имя, фамилия", каждый из которых соответствует одному из двух столбцов модели таблицы (конечная цель - сделать заказ гибким, но на данный момент я просто хочу, чтобы это работает). Текущие способы, которыми я пытаюсь идти об этом:
а) объединить результаты из двух столбцов в прокси-сервер, третий столбец, который просто имеет имя, фамилию в виде дискретной строки (что неудобно, если пользователь вводит его в качестве фамилии, имени. Текущая реализация не работает, когда я пытаюсь чтобы сделать setCompletionColumn для столбца "поддельные", он не активирует мой метод переопределенных данных. columnCount включает поддельный столбец, хотя)
б) иметь Completer, который не фильтрует, с прокси-моделью бэкэнда, фильтр acceptptsrow() которого фильтрует как первый, так и последний (не знаю, как сделать нефильтрующий завершитель - иначе он просто просматривает один столбец и всплывающее окно в конце показывает кучу имен без фамилий)
c) подделать модель дерева и сделать так, чтобы, когда я ставил запятую, модель просматривала "ветку", состоящую из всех людей, чьи первые ИЛИ фамилии (начинаются с?) являются строкой перед запятой. (Я просто не уверен, как начать с этого, он использует тот же тип acceptptsrow, что и b), но я не знаю, как сделать, как отдельные ветви ~)
это вызывает у меня головную боль, и ни одна из этих реализаций не особенно приятна. Я делаю это слишком тяжело для себя и люблю использовать неправильный инструмент, или это просто то, к чему я должен пристегнуться? Я могу вставить некоторый код, но мне интересно, стоит ли то, что я делаю.
обновление: фрагменты кода и пикс
] 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
и в идеале это будет выглядеть примерно так:
1 ответ
Стратегия состоит в том, чтобы использовать прокси-сервер, в котором создается новая роль, которая будет возвращать объединенные тексты, чтобы можно было увидеть всплывающее окно с объединенными текстами, и мы создадим собственный делегат для 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_())