Ian Bailey-Mortimer
Ian Bailey-Mortimer

Reputation: 345

QCompleter in QLineEdit has no useable height and not showing matches

First time I'm trying to use a QCompleter in PyQt5.

My data is a list of dictionary objects with two text fields. I'm using a subclassed QAbstractTableModel to return a combined string in column 0. I'm giving this to a QCompleter in a QLineEdit. But when I try to complete, it gives me an empty popup. When I press up/down, it does go through the options (I have checked using print() statements in the data() method of my QAbstractTableModel) but only occasionally does it put a completed string in the text of the QLineEdit. enter image description here

Note the popup stays empty, even when it puts a completed string in the text. enter image description here

I want a "normal" popup list, like all the examples I can find online show. What am I doing wrong?

allSubjects = []

def loadAllSubjects():
    global allSubjects
    allSubjects = []

    cur.execute('''
        select trim(sub_code), trim(sub_desc)
        from subtab
        where cmpy_code='01' and active_flg='Y'
        order by sub_desc, sub_code
    ''')
    row = cur.fetchone()
    while row:
        (sub_code, sub_desc) = row
        allSubjects.append({
            "code": sub_code,
            "desc": sub_desc,
        })
        row = cur.fetchone()


class SubjectTableModel(QtCore.QAbstractTableModel):
    def __init__(self):
        super(SubjectTableModel, self).__init__()
    
    def rowCount(self, index) -> int:
        global allSubjects
        return len(allSubjects)
    
    def columnCount(self, index) -> int:
        return 4

    def data(self, index, role):
        # print(f"data({index.row()},{index.column()})")
        global allSubjects
        sub = allSubjects[index.row()]
        # print(sub)
        colidx = index.column()
        if colidx == 0:
            return sub["desc"] + " (" + sub["code"] + ")"
        elif colidx == 1:
            return sub
        elif colidx == 2:
            return sub["code"]
        elif colidx == 3:
            return sub["desc"]
            
    def headerData(self, colidx, orientation, role):
        if colidx == 0:
            return "Search"
        elif colidx == 1:
            return "Code"
        elif colidx == 2:
            return "Description"

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.resize(1000, 800)

        subLabel = QLabel("Subject:")
        subSearch = QLineEdit()
        
        subCompleter = QCompleter()
        subCompleter.setModel(SubjectTableModel())
        subCompleter.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
        # subCompleter.setModelSorting(QCompleter.CaseInsensitivelySortedModel)

        subSearch.setCompleter(subCompleter)

        subPart = QHBoxLayout()
        subPart.addWidget(subLabel)
        subPart.addWidget(subSearch)

(...)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)


app = QApplication(sys.argv)

window = MainWindow()
window.show()
loadAllSubjects()
app.exec()

Edit: I know it has something to do with using a QAbstractTableModel for my data. If I just use a list of strings instead, it works fine.

Upvotes: 0

Views: 108

Answers (1)

IoeCmcomc
IoeCmcomc

Reputation: 196

You can limit what to return based on the role parameter of the data method:

def data(self, index, role):
    global allSubjects
    sub = allSubjects[index.row()]
    if role == Qt.DisplayRole or role == Qt.EditRole:
        colidx = index.column()
        if colidx == 0:
            return sub["desc"] + " (" + sub["code"] + ")"
        elif colidx == 1:
            return sub["code"]
        elif colidx == 2:
            return sub["desc"]

Qt.DisplayRole controls the text to show up in the popup, while Qt.EditRole controls the actual text inserted into the line edit.

result

Upvotes: 0

Related Questions