简体   繁体   English

使用QCompleter时程序退出并出错

[英]Program quit with error when using QCompleter

I'm writing a program with pyqt5 and want to make the QlineEdit show history input by using sqlite to store the inputs. 我正在用pyqt5编写一个程序,并希望通过使用sqlite来存储输入来创建QlineEdit显示历史输入。 I use a signal to catch the cursor when focusInEvent happens and select the history records at that time, then I put the results into QCompleter so it can pop up in QlineEdit. 当focusInEvent发生时我使用一个信号来捕捉光标并选择那时的历史记录,然后我把结果放到QCompleter中,这样它就可以在QlineEdit中弹出。 Now I can make the history inputs show in QlineEdit object but when I click on any value, 1s later, the whole program quit automatically with error, which says "Python has stopped". 现在我可以在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

It is difficult to analyze where the problem is without you provide an MCVE, so my response implements what you require without taking into account your current code for it must meet the following requirements: 如果没有提供MCVE,很难分析问题所在,因此我的响应会实现您的需求,而不考虑您当前的代码,因为它必须满足以下要求:

  • You must have 2 tables: objects and history that are related. 您必须有2个表:相关的对象和历史记录。
  • The table objects saves the name of the filtering history, it is similar to the use of the objectName that you use, but in general 2 widgets can access the same history if the connection establishes the same name 表对象保存过滤历史的名称,它类似于您使用的objectName的使用,但通常如果连接建立相同的名称,则两个小组件可以访问相同的历史记录
  • In the history table the information of the words associated with the id of the objects table is saved. 在历史表中,保存与对象表的id相关联的单词的信息。

In my example I use the following instructions to create them: 在我的示例中,我使用以下说明创建它们:

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));

Also to test it I created a data, if you already have data then you must eliminate the if test: and everything inside. 另外为了测试它我创建了一个数据,如果你已经拥有数据,那么你必须消除if test:以及里面的一切。

Finally for a better understanding of my solution I show a QTableView that is changing according to the selection. 最后为了更好地理解我的解决方案,我展示了一个根据选择而改变的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_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM