Программа завершилась с ошибкой при использовании QCompleter - PullRequest
0 голосов
/ 23 марта 2019

Я пишу программу с pyqt5 и хочу, чтобы QlineEdit показывал ввод истории, используя sqlite для хранения вводов. Я использую сигнал, чтобы поймать курсор, когда происходит focusInEvent, и выбираю записи истории в это время, затем помещаю результаты в QCompleter, чтобы он мог появиться в QlineEdit. Теперь я могу сделать так, чтобы входы истории отображались в объекте QlineEdit, но когда я щелкаю любое значение, спустя 1 с, вся программа автоматически завершает работу с ошибкой, которая говорит: «Python остановился».

class FocusLineEdit(QLineEdit):
    ac = pyqtSignal(list)
    def __init__(self, parent=None):
        super(FocusLineEdit, self).__init__(parent)
        self.ac.connect(self.addCompleter)

    def focusInEvent(self, event):
        rtl = call_history(self.objectName())
        self.ac.emit(rtl)

    def addCompleter(self, rtl):
        self.autoCompleter = QCompleter(rtl)
        self.autoCompleter.setCompletionMode(1)
        self.setCompleter(self.autoCompleter)

    def focusOutEvent(self, event):
        pass

1 Ответ

0 голосов
/ 24 марта 2019

Трудно проанализировать, в чем проблема, если вы не предоставите MCVE, поэтому мой ответ реализует то, что вам нужно, без учета вашего текущего кода, поскольку он должен отвечать следующим требованиям:

  • У вас должно быть 2 таблицы: связанные объекты и история.
  • Объекты таблицы сохраняют имя истории фильтрации, оно аналогично использованию используемого имени объекта, но в целом 2 виджета могут обращаться к одной и той же истории, если соединение устанавливает одно и то же имя
  • В таблице истории сохраняется информация слов, связанных с идентификатором таблицы объектов.

В моем примере я использую следующие инструкции для их создания:

CREATE TABLE IF NOT EXISTS objects (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE);
CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, id_object INTEGER REFERENCES objects (id), word TEXT NOT NULL, UNIQUE (id_object, word));

Также, чтобы проверить это, я создал данные, если у вас уже есть данные, вы должны удалить if test: и все внутри.

Наконец, для лучшего понимания моего решения я показываю QTableView, который меняется в зависимости от выбора.

from PyQt5 import QtCore, QtGui, QtWidgets, QtSql

def createConnection():
    db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
    db_path = 'test.db'
    db.setDatabaseName(db_path)
    if not db.open():
        QMessageBox.critical(None, qApp.tr("Cannot open database"),
                             qApp.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

    test = True
    if test:
        query = QtSql.QSqlQuery()
        if not query.exec_('CREATE TABLE IF NOT EXISTS objects (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE);'):
            return False
        if not query.exec_('CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, id_object INTEGER REFERENCES objects (id), word TEXT NOT NULL, UNIQUE (id_object, word));'):
            return False

        for i in range(3):
            query.prepare('INSERT INTO objects (name) VALUES (?)')
            query.addBindValue("obj{}".format(i))
            if not query.exec_():
                print(query.lastError().text())
        import requests
        import random
        word_site = "http://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-type=text/plain"
        response = requests.get(word_site)
        WORDS = response.content.decode().splitlines()
        print(WORDS)
        for i in range(3):
            for text in random.sample(WORDS, 50):
                query.prepare('INSERT INTO history (id_object, word) VALUES (?, ?)')
                query.addBindValue(i+1)
                query.addBindValue(text)
                if not query.exec_():
                    print(query.lastError().text())
    return True

class Completer(QtWidgets.QCompleter):
    def __init__(self, parent=None):
        super(Completer, self).__init__(parent)
        self._last_words = []

    def splitPath(self, path):
        if path[-1] != ' ':
            words = path.split()
            self._last_words = words[:-1] if len(words) > 1 else []
            return [words[-1]]
        else:
            QtCore.QTimer.singleShot(0, self.popup().hide)
            return []

    def pathFromIndex(self, index):
        val = super(Completer, self).pathFromIndex(index)
        return ' '.join(self._last_words + [val])

class HistoryManager(QtCore.QObject):
    nameChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super(HistoryManager, self).__init__(parent)
        model = QtSql.QSqlRelationalTableModel(self)
        model.setTable("history")
        model.setRelation(1, QtSql.QSqlRelation("objects", "id", "name"))
        model.select()

        self._proxy = QtCore.QSortFilterProxyModel(self)
        self._proxy.setSourceModel(model)
        self._proxy.setFilterKeyColumn(1)
        # proxy.setFilterFixedString("obj1")

        self._widgets = {}
        self._completer = Completer(self)
        self._completer.setModel(self._proxy)
        self._completer.setCompletionColumn(2)

    def register_widget(self, widget, objectname):
        # TODO
        if callable(getattr(widget, "setCompleter")):
            widget.installEventFilter(self)
            self._widgets[widget] = objectname
            return True
        return False

    def eventFilter(self, obj, event):
        if obj in self._widgets:
            if event.type() == QtCore.QEvent.FocusIn:
                name = self._widgets[obj]
                self._proxy.setFilterFixedString(name)
                obj.setCompleter(self._completer)
                self.nameChanged.emit(name)
            elif event.type() == QtCore.QEvent.FocusOut:
                obj.setCompleter(None)
                self._proxy.setFilterFixedString("")
                self.nameChanged.emit("")
        return super(HistoryManager, self).eventFilter(obj, event)

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self._manager = HistoryManager()

        model = QtSql.QSqlRelationalTableModel(self)
        model.setTable("history")
        model.setRelation(1, QtSql.QSqlRelation("objects", "id", "name"))
        model.select()

        self._proxy = QtCore.QSortFilterProxyModel(self)
        self._proxy.setSourceModel(model)
        self._proxy.setFilterKeyColumn(1)

        tv = QtWidgets.QTableView()
        tv.setModel(self._proxy)

        vlay = QtWidgets.QVBoxLayout()
        for i in range(3):
            le = QtWidgets.QLineEdit()
            vlay.addWidget(le)
            self._manager.register_widget(le, "obj{}".format(i))
        vlay.addStretch()
        lay = QtWidgets.QHBoxLayout(self)
        lay.addWidget(tv, stretch=1)
        lay.addLayout(vlay)

        self._manager.nameChanged.connect(self._proxy.setFilterFixedString)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    if not createConnection():
        sys.exit(-1)
    manager = HistoryManager()
    w = Widget()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())
...