【问题标题】:PyQt5 QCompleter for QLineEdit crashing with no exception visible用于 QLineEdit 的 PyQt5 QCompleter 崩溃,没有异常可见
【发布时间】:2020-01-24 13:11:19
【问题描述】:

我正在尝试使用 QLineEdit 的 QCompleter 类在键入时提供自动完成建议,并在用户输入新文本后更新建议。但是,当我尝试使用以完成者列表中已经存在的内容开头的文本来更新完成者时,它只会使整个应用程序崩溃,没有可见的异常!甚至 try-except 也没有捕捉到这个错误,我无法理解我做错了什么......

下面是我的代码的一个简单示例:它是一个简单的“回显”控制台应用程序,它从 QLineEdit(输入文本框)获取命令并将其写入 QTextBrowser(输出文本框)。当输入全新的“命令”(文本)时,它工作正常,并被添加到完成者中,所以下次我可以看到它。但是,如果新文本的开头与完成列表中的其他词相似,则选择它会使整个 GUI 应用程序崩溃,没有可见的异常,即使我在调试模式下运行...

请看下面我的例子,并尝试在上面的文本框选项写:a、aa、aaa(开头类似于完成词:aaa1)

我做错了什么??

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLineEdit, QTextBrowser, QCompleter

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

        self.setWindowTitle('console')
        self.setGeometry(10, 50, 500, 800)

        # Create text box for input
        self.consoleCommandLineEdit = QLineEdit(self)
        self.consoleCommandLineEdit.setFixedHeight(25)
        self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
        self.completerCommands = ['aaa1','aaa2','aaa3'] # initial completer list
        completer = QCompleter(self.completerCommands)
        self.consoleCommandLineEdit.setCompleter(completer)

        # Create text box for output
        self.consoleViewer = QTextBrowser(self)
        self.consoleViewer.setLineWrapMode(QTextBrowser.NoWrap)

        widget = QWidget(self)
        self.setCentralWidget(widget)
        self.vlay = QVBoxLayout(widget)
        self.vlay.addWidget(self.consoleCommandLineEdit)
        self.vlay.addWidget(self.consoleViewer)

    def gotConsoleCommand(self):
        cmd = self.consoleCommandLineEdit.text()
        self.consoleCommandLineEdit.setText('')
        self.sendCommandToConsole(cmd)

    def sendCommandToConsole(self,cmd):
        self.consoleViewer.append(cmd) # add cmd to output box
        if cmd not in self.completerCommands: # if the command is new, add it to the completer
            self.completerCommands.append(cmd)                  # 1. add the new text to the list we have
            completer = QCompleter(self.completerCommands)      # 2. create a new completer object
            self.consoleCommandLineEdit.setCompleter(completer) # 3. set the new completer as the LineEdit completer


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt5 qcompleter


    【解决方案1】:

    我还没有找到问题的原因,但是比每次需要添加新文本时都创建一个新的 QCompleter 更好的解决方案。在这种情况下,最好使用模型来存储作为 QCompleter 信息基础的文本。

    import sys
    from PyQt5.QtGui import QStandardItem, QStandardItemModel
    from PyQt5.QtWidgets import (
        QApplication,
        QWidget,
        QMainWindow,
        QVBoxLayout,
        QLineEdit,
        QTextBrowser,
        QCompleter,
    )
    
    
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
    
            self.setWindowTitle("console")
            self.setGeometry(10, 50, 500, 800)
    
            # Create text box for input
            self.consoleCommandLineEdit = QLineEdit()
            self.consoleCommandLineEdit.setFixedHeight(25)
            self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
    
            self.model = QStandardItemModel()
            self.model.appendRow([QStandardItem(text) for text in ("aaa1", "aaa2", "aaa3")])
            completer = QCompleter(self.model, self)
            self.consoleCommandLineEdit.setCompleter(completer)
    
            # Create text box for output
            self.consoleViewer = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap)
    
            widget = QWidget()
            self.setCentralWidget(widget)
            vlay = QVBoxLayout(widget)
            vlay.addWidget(self.consoleCommandLineEdit)
            vlay.addWidget(self.consoleViewer)
    
        def gotConsoleCommand(self):
            cmd = self.consoleCommandLineEdit.text()
            self.consoleCommandLineEdit.clear()
            self.sendCommandToConsole(cmd)
    
        def sendCommandToConsole(self, cmd):
            self.consoleViewer.append(cmd)  # add cmd to output box
            if not self.model.findItems(cmd):
                self.model.appendRow(QStandardItem(cmd))
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit(app.exec_())
    

    也许有助于我们理解问题的是,如果您将父级添加到 QCompleter,则问题不会发生:

    completer = QCompleter(self.completerCommands, <b>self</b>)

    但我强调:最好的解决方案是使用模型。

    【讨论】:

    • 问题与弹出视图有关:如果将完成模式设置为内联,则可以正常工作。某些项目视图操作因视觉布局和动画而延迟(请参阅 QAbstractItemView 源代码中的 executePostedLayout 调用);在设置新的完成后,旧的(及其模型)被垃圾收集,因为没有父或其他 python 引用,但视图仍在尝试更新自身,因此(我相信)崩溃。
    • @musicamante 你在怀疑,内存同步不完美
    • 感谢 eyllanesc!您的解决方案完美运行!我会采用你的方法。我以前不知道那些“模型”对象,也不知道如何更新现有的 Completer。
    • 顺便说一句,您知道在选择完成者的条目后如何清除 QLineEdit 吗?当我开始输入时,然后从列表中选择一个选项(使用向下/向上箭头)并按 ENTER,文本将添加到下面的列表中,但仍保留在上方的文本框中......虽然我运行了命令: self.consoleCommandLineEdit.clear()
    • @Koby.G 我发现您的解释不清楚,您希望何时将其添加到列表中:当您从弹出窗口中选择一个项目并按 Enter 时,或者当您输入 QLineEdit 并按 Enter 时?
    猜你喜欢
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-29
    • 2013-09-15
    • 1970-01-01
    相关资源
    最近更新 更多