PDA

View Full Version : Unable to add a QCompleter to a QCombobox in PyQt5



TheGrudge
8th March 2019, 17:25
Hi,

I have the following code:


import sys
from functools import partial
from typing import Optional

from PyQt5 import QtCore
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, QObject, QSortFilterProxyModel, QVariant, \
Qt
from PyQt5.QtWidgets import QAbstractItemView, QApplication, QComboBox, QHBoxLayout, QLabel, QTableView, \
QWidget


class MyModel(QAbstractTableModel):

def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent=parent)

self.headers = ['Nummer', 'Name', 'Gemeinde']
self.data = [
('010006', 'Alt Duvenstedt', 'Alt Duvenstedt'),
('010010', 'Altenhof', 'Altenhof'),
('010012', 'Altwickenbeck (Neuw)', 'Neuwittenbeck'),
('010021', 'Bargstedt', 'Bargstedt'),
('010023', 'Barkelsby', 'Barkelsby'),
('010024', 'Basdorf (Rie)', 'Rieseby'),
]

def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.headers[section]

def columnCount(self, parent: QModelIndex = ...) -> int:
return len(self.headers)

def rowCount(self, parent: QModelIndex = ...) -> int:
return len(self.data)

def data(self, index: QModelIndex, role: int = ...):
if role == Qt.DisplayRole:
return self.data[index.row()][index.column()]
return QVariant()


class MyCombobox(QComboBox):

def __init__(self, parent: Optional[QWidget] = None) -> None:
super().__init__(parent=parent)

self.setFocusPolicy(Qt.StrongFocus)
# self.setEditable(True)

# self.filterModel = QSortFilterProxyModel(self)
# self.filterModel.setFilterCaseSensitivity(Qt.CaseI nsensitive)

self.tableView = QTableView(self)
self.setView(self.tableView)
self.tableView.setSelectionMode(QAbstractItemView. SingleSelection)
self.tableView.setSelectionBehavior(QAbstractItemV iew.SelectRows)
self.tableView.setSortingEnabled(True)
self.tableView.setAutoScroll(False)
self.tableView.horizontalHeader().setStretchLastSe ction(True)
self.tableView.verticalHeader().hide()

# self.completer = QCompleter(self)
# self.completer.setCompletionMode(QCompleter.Unfilt eredPopupCompletion)
# self.completer.setPopup(self.tableView)
# self.completer.setModel(self.filterModel)
# self.setCompleter(self.completer)

# self.lineEdit().textEdited.connect(self.filterMode l.setFilterFixedString)
# self.completer.activated.connect(self.onCompleterA ctivated)

def setModel(self, model: QtCore.QAbstractItemModel) -> None:
super().setModel(model)
# self.filterModel.setSourceModel(model)
# self.completer.setModel(self.filterModel)

self.tableView.resizeColumnsToContents()
self.tableView.resizeRowsToContents()
self.tableView.setMinimumWidth(self.tableView.hori zontalHeader().length())
self.setMinimumWidth(100)

def setModelColumn(self, column):
super().setModelColumn(column)
# self.filterModel.setFilterKeyColumn(column)
# self.completer.setCompletionColumn(column)

# def onCompleterActivated(self, text):
# if text:
# index = self.findText(str(text))
# self.setCurrentIndex(index)


def setModelIndex(index: int, target: QComboBox):
target.blockSignals(True)
target.setCurrentIndex(index)
target.blockSignals(False)


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

model = MyModel()
sortModel = QSortFilterProxyModel()
sortModel.setFilterCaseSensitivity(Qt.CaseInsensit ive)
sortModel.sort(1, Qt.AscendingOrder)
sortModel.setSourceModel(model)

combobox = MyCombobox()
combobox.setModel(sortModel)
combobox.setModelColumn(0)

combobox2 = MyCombobox()
combobox2.setModel(sortModel)
combobox2.setModelColumn(1)

combobox.currentIndexChanged.connect(partial(setMo delIndex, target=combobox2))
combobox2.currentIndexChanged.connect(partial(setM odelIndex, target=combobox))

widget = QWidget()
layout = QHBoxLayout()
layout.addWidget(QLabel('Nummer'))
layout.addWidget(combobox)
layout.addWidget(QLabel('Name'))
layout.addWidget(combobox2)
layout.addStretch(10)

widget.setWindowTitle('Synchronized comboboxes with shared model')
widget.setFixedWidth(500)
widget.setLayout(layout)
widget.show()

sys.exit(app.exec_())


I have a model which is shared between two comboboxes.
If I select an entry in one combobox, the other combobox should be updated accordingly.
This works fine when the combobox is not set editable.

As soon as I set it editable (uncommenting the code), I am unable to fill the combobox correctly.

There a several issues I don't understand:


The tableview can not be used twice (I guess I have to duplicate the code and create two instances of a tableview, one for the combobox itself and one for the completer?)
If I select something in the tableview of the combobox, the lineedit is not updated
If I connect the textEdited signal of the lineedit and try to change the index "manually", the changes still are not reflected in the combobox.


I removed all completer code as well and only wanted to have an editable combobox with a tableview, but this does not work as well. Only if I set editable to False I have a working combobox again (selecting something in the tableview will set the index and display the correct text).

What am I doing wrong? I can't figure out what the problem is.

The code above should be a working example with two synched readonly comboboxes.
I just want to add autocompletion / filtering to both of them, because this tableview will contain thousands of items later on.

TheGrudge
8th March 2019, 19:38
It seems that adding the EditRole to the model seems to work...
I updated the model like this:


def data(self, index: QModelIndex, role: int = ...):
if role == Qt.DisplayRole or role == Qt.EditRole:
return self.data[index.row()][index.column()]
return QVariant()

Now everything seems to work fine and I don't need to access self.lineEdit() and set its text...