使用QLineEdit和QItemDelegate完成

时间:2018-03-09 12:29:22

标签: python-3.x qt5 pyqt5

我尝试实现我们可以在widgets / tools / customcompleter源中找到的QCompleter示例。 我想要的是当我键入一些文本时,如果有一些匹配,则选择第一个可用的完成行,这样如果我点击返回键(并且只有我点击它),则完成完成。

但在我的代码中,该行永远不会被选中。我认为我的问题位于keyPressEvent,但我不知道在哪里。这是一个最小的例子,用pyqt5(5.10)和Python 3.5.2编写。

非常感谢任何帮助:)

祝你好运

import sys

from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant
from PyQt5.QtWidgets import (QApplication, QCompleter, QItemDelegate,
                             QLineEdit, QMainWindow, QTableView)


class MyLineEdit(QLineEdit):

    def __init__(self, parent=None, completer=None):
        super().__init__(parent)
        if completer:
            self.setCompleter(completer)

    def setCompleter(self, completer):
        if completer:
            completer.setWidget(self)
            completer.setCompletionMode(QCompleter.PopupCompletion)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setModelSorting(
                QCompleter.CaseSensitivelySortedModel)
            completer.setMaxVisibleItems(15)
            completer.activated.connect(self.insertCompletion)

        super().setCompleter(completer)

    def insertCompletion(self, completion):
        completer = self.completer()
        if completer and completer.widget() == self:
            completer.widget().setText(completion)

    def keyPressEvent(self, event):

        completer = self.completer()

        if event.key() in (Qt.Key_Return, Qt.Key_Enter,
                           Qt.Key_Tab, Qt.Key_Backtab):
            self.returnPressed.emit()
            if completer and completer.popup().isHidden():
                return

        super().keyPressEvent(event)
        input_text = self.text()

        if completer:

            if not event.text():
                completer.popup().hide()
                return

            if input_text and input_text != completer.completionPrefix():
                completer.setCompletionPrefix(input_text)
                completer.popup().setCurrentIndex(
                    completer.completionModel().index(0, 0))


class MyDelegate(QItemDelegate):

    def __init__(self, parent):
        super().__init__(parent)

    def createEditor(self, parent, option, index):
        strings = ('tata', 'tete', 'titi', 'toto', 'tutu')
        completer = QCompleter(strings)
        editor = MyLineEdit(parent)
        editor.setCompleter(completer)
        editor.editingFinished.connect(self.commitAndCloseEditor)
        editor.returnPressed.connect(self.commitAndCloseEditor)
        return editor

    def commitAndCloseEditor(self):
        editor = self.sender()
        self.commitData.emit(editor)
        self.closeEditor.emit(editor)

    def setEditorData(self, editor, index):
        if editor:
            editor.setText(index.model().data[0])

    def setModelData(self, editor, model, index):
        if editor:
            model.setData(index, editor.text(), Qt.EditRole)


class Model(QAbstractTableModel):

    def __init__(self):
        super().__init__()
        self.data = ['hello']

    def rowCount(self, parent=None):
        return 1

    def columnCount(self, parent=None):
        return 1

    def data(self, index, role):
        if not index.isValid():
            return QVariant()

        if role in (Qt.DisplayRole, Qt.EditRole):
            return self.data[0]

        return QVariant()

    def setData(self, index, value, role):
        if role == Qt.EditRole:
            self.data[0] = value

            top_left = self.index(0, 0)
            bottom_right = self.index(
                self.rowCount() + 1, self.columnCount())
            self.dataChanged.emit(top_left, bottom_right,
                                  [Qt.DisplayRole])
            return True

        return False

    def flags(self, index):
        return Qt.ItemIsEditable | super().flags(index)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.model = Model()
        self.table = QTableView()
        self.table.setModel(self.model)
        delegate = MyDelegate(self.table)
        self.table.setItemDelegateForColumn(0, delegate)

    def initUI(self):
        self.show()

        self.setCentralWidget(self.table)


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

    mw = MainWindow()
    mw.initUI()

    sys.exit(app.exec_())

2 个答案:

答案 0 :(得分:0)

试一试:

import sys
from PyQt5 import QtCore, QtWidgets 

class LineEdit(QtWidgets.QLineEdit):
    def __init__(self, *args, **kwargs):

        QtWidgets.QLineEdit.__init__(self, *args, **kwargs)
        self.multipleCompleter = None

    def keyPressEvent(self, event):
        QtWidgets.QLineEdit.keyPressEvent(self, event)

        if not self.multipleCompleter:
            return

        c = self.multipleCompleter

        if self.text() == "":
            return

        c.setCompletionPrefix(self.cursorWord(self.text()))

        if len(c.completionPrefix()) < 1:
            c.popup().hide()
            return

        c.complete()

    def cursorWord(self, sentence):
        p = sentence.rfind(" ")
        if p == -1:
            return sentence
        return sentence[p + 1:]

    def insertCompletion(self, text):
        p = self.text().rfind(" ")
        if p == -1:
            self.setText(text)
        else:
            self.setText(self.text()[:p+1]+ text)

    def setMultipleCompleter(self, completer):
        self.multipleCompleter = completer
        self.multipleCompleter.setWidget(self)
        completer.activated.connect(self.insertCompletion)


def main():
    app = QtWidgets.QApplication(sys.argv)
    w   = LineEdit()
    completer = QtWidgets.QCompleter(['tata', 'tete', 'titi', 'toto', 'tutu'])
    completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
    w.setMultipleCompleter(completer)
    w.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

答案 1 :(得分:0)

终于找到了解决方案。我使用了QTextEdit,它不能与QLineEdit一起使用,我不知道为什么。我还有一个小问题,当我按下返回键时我无法关闭完成弹出窗口,所以我必须按两次返回键。不是一个大问题。

import sys

from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, pyqtSignal
from PyQt5.QtGui import QTextCursor, QTextOption
from PyQt5.QtWidgets import (QAbstractItemDelegate, QApplication, QCompleter,
                             QItemDelegate, QMainWindow, QTableView, QTextEdit)


class MyLineEdit(QTextEdit):

    returnPressed = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptRichText(False)
        self.setWordWrapMode(QTextOption.NoWrap)
        self.setUndoRedoEnabled(False)
        self.setTabChangesFocus(True)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.completer = None
        self.textChanged.connect(self.textHasChanged)

    def setCompleter(self, completer):
        if completer:
            completer.setWidget(self)
            completer.setCompletionMode(QCompleter.PopupCompletion)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setModelSorting(
                QCompleter.CaseSensitivelySortedModel)
            completer.setMaxVisibleItems(15)
            completer.activated.connect(self.insertCompletion)
            self.completer = completer

    def insertCompletion(self, completion):
        print('>>> insertCompletion')
        if self.completer and self.completer.widget() == self:
            self.completer.widget().setPlainText(completion)
            self.completer.widget().moveCursor(QTextCursor.EndOfLine)
            self.completer.widget().ensureCursorVisible()

    def focusInEvent(self, event):
        print('>>> focusInEvent')
        if self.completer:
            self.completer.setWidget(self)
        super().focusInEvent(event)

    def keyPressEvent(self, event):
        print('>>> keyPressEvent')

        if self.completer and self.completer.popup().isVisible():

            if event.key() in (Qt.Key_Return, Qt.Key_Enter,
                               Qt.Key_Tab, Qt.Key_Backtab, Qt.Key_Escape):
                event.ignore()
                return
        else:
            if event.key() in(Qt.Key_Return, Qt.Key_Enter):
                self.returnPressed.emit()
                return

        super().keyPressEvent(event)

        if not self.toPlainText():
            self.completer.popup().hide()
            return

        self.completer.setCompletionPrefix(self.toPlainText())
        self.completer.popup().setCurrentIndex(
            self.completer.completionModel().index(0, 0))
        self.completer.complete()

    def textHasChanged(self):
        # remove new lines and strip left blank characters
        self.blockSignals(True)
        cursor = self.textCursor()
        self.setPlainText(' '.join(self.toPlainText().splitlines()).lstrip())
        self.setTextCursor(cursor)
        self.ensureCursorVisible()
        self.blockSignals(False)


class MyDelegate(QItemDelegate):

    def __init__(self, parent):
        super().__init__(parent)

    def createEditor(self, parent, option, index):
        strings = ('tata', 'tada', 'tadam', 'tete', 'titi', 'toto', 'tutu')
        completer = QCompleter(strings)
        editor = MyLineEdit(parent)
        editor.setCompleter(completer)
        editor.returnPressed.connect(self.commitAndCloseEditor)
        return editor

    def commitAndCloseEditor(self):
        print('>>> commitAndCloseEditor')
        editor = self.sender()
        self.commitData.emit(editor)
        self.closeEditor.emit(editor)

    def setEditorData(self, editor, index):
        if editor:
            editor.setText(index.model().data[0])
            editor.selectAll()

    def setModelData(self, editor, model, index):
        if editor:
            model.setData(index, editor.toPlainText(), Qt.EditRole)


class Model(QAbstractTableModel):

    def __init__(self):
        super().__init__()
        self.data = ['hello']

    def rowCount(self, parent=None):
        return 1

    def columnCount(self, parent=None):
        return 1

    def data(self, index, role):
        if not index.isValid():
            return QVariant()

        if role in (Qt.DisplayRole, Qt.EditRole):
            return self.data[0]

        return QVariant()

    def setData(self, index, value, role):
        if role == Qt.EditRole:
            self.data[0] = value

            top_left = self.index(0, 0)
            bottom_right = self.index(
                self.rowCount() + 1, self.columnCount())
            self.dataChanged.emit(top_left, bottom_right,
                                  [Qt.DisplayRole])
            return True

        return False

    def flags(self, index):
        return Qt.ItemIsEditable | super().flags(index)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.model = Model()
        self.table = QTableView()
        self.table.setModel(self.model)
        delegate = MyDelegate(self.table)
        self.table.setItemDelegateForColumn(0, delegate)

    def initUI(self):
        self.show()

        self.setCentralWidget(self.table)


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

    mw = MainWindow()
    mw.initUI()

    sys.exit(app.exec_())