首页 > 解决方案 > 为什么 QTextEdit 有时会在 Ubuntu 20.04 上多次发出 textChanged?

问题描述

我正在使用 PyQT5 和 Python 3.8。我在 Ubuntu 20.04 和 Windows 10 上运行完全相同的代码,使用 pip 从相同的 requirements.txt 文件安装相同的依赖项,其内容是:

mistune==0.8.4
PyQt5==5.15.0
PyQt5-sip==12.8.0
PyQt5-stubs==5.14.2.2

出于某种原因,当我在 Ubuntu 上运行我的应用程序时,应该第二次发出 textChanged 信号,它会发出两次。

例如,预期的行为是这样的:

Action triggered first time:
    textChanged signal emitted

Action triggered second time:
    textChanged signal emitted

Action triggered third time:
    textChanged signal emitted

Action triggered fourth time:
    textChanged signal emitted

...

这确实是在 Windows 上发生的事情。没有重复的信号发射。但是在 Ubuntu 上,我得到了这个:

Action triggered first time:
    textChanged signal emitted

Action triggered second time:
    textChanged signal emitted
    textChanged signal emitted

Action triggered third time:
    textChanged signal emitted

Action triggered fourth time:
    textChanged signal emitted

...

同样,来自同一存储库的相同代码,从 pip 安装的相同依赖项。我知道的设置的一个区别是 Ubuntu 根本不会运行该应用程序,除非我还使用 APT 而不是 pip3 安装了 PyQT5 sudo apt install python3-pyqt5,因为由于某种原因 pip 不包含所需的 xcb 文件让 Qt 在 Ubuntu 上运行。

只有在第二次应该发出信号时,我才看到它发出了两次。不是在任何其他场合。但它始终以这种方式行为不端。

我很困惑。至于对我的应用程序的实际影响,在实践中没问题;它只是一次错误地设置了一个未保存的更改标志,因为不需要的信号发射发生在另一个标志之外。但如果可能的话,我真的很想弄清楚这一点,以防它有我没有看到的其他后果,或者如果它证明对其他一些应用程序有问题。


为最小可重现示例进行了编辑。做这个帮助我缩小了范围,所以谢谢你鼓励我不要那么懒惰。似乎正在发生的是,由于某种原因,如果将QTextEdit置于只读状态,然后切换只读状态,然后设置焦点,textChanged则出于某种原因发出信号,但仅发出一次。只读状态的进一步切换和焦点更改QTextEdit不会触发这种额外的信号发射。

这在 Windows 上不会发生,这是预期的行为。

import sys

from PyQt5.QtWidgets import QApplication, QTextEdit, QMainWindow, QPushButton, QFrame, QGridLayout

TextChangedCounter = 0

def StartApp():
    AppInst = QApplication(sys.argv)

    def TextChanged():
        global TextChangedCounter
        TextChangedCounter += 1
        print("textChanged emitted", TextChangedCounter, "times.")

    def ToggleReadOnly():
        TextEdit.setReadOnly(not TextEdit.isReadOnly())
        if not TextEdit.isReadOnly():
            TextEdit.setFocus()

    MainWindow = QMainWindow()

    Frame = QFrame()

    TextEdit = QTextEdit()
    TextEdit.setReadOnly(True)
    TextEdit.textChanged.connect(TextChanged)

    ToggleButton = QPushButton("Toggle Read Only")
    ToggleButton.clicked.connect(ToggleReadOnly)

    Layout = QGridLayout()
    Layout.addWidget(TextEdit)
    Layout.addWidget(ToggleButton)
    Frame.setLayout(Layout)

    MainWindow.setCentralWidget(Frame)

    MainWindow.show()

    sys.exit(AppInst.exec_())


if __name__ == "__main__":
    StartApp()

所以要明确:在 Windows 10 上正确发生的预期行为是单击标有“Toggle Read Only”的按钮不应打印“textChanged 发出 1 次”。但是,在 Ubuntu 20.04 上,此消息是通过单击按钮打印的,但只是第一次。


编辑以添加其他内容:显然在切换只读状态之前将焦点设置在 QTextEdit 上会延迟这种额外的信号发射,直到您实际在 QTextEdit 中键入某些内容,如下面的更改函数中所示。但是…… 为什么

def ToggleReadOnly():
    if TextEdit.isReadOnly():
        TextEdit.setFocus()
    TextEdit.setReadOnly(not TextEdit.isReadOnly())
    if not TextEdit.isReadOnly():
        TextEdit.setFocus()

编辑以在评论中添加建议结果。在 venv 中删除 APT 安装python3-pyqt5并重新安装 pip 版本会导致以下输出,这正是导致我最初安装 APT 版本的原因:

qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, webgl, xcb.

有两件事导致 GUI 完全运行:

  1. 在 venv 中安装 PyQT5 的 APT 版本(显然是 5.14.1)sudo install python3-pyqt5以及 pip 版本 5.15.0。我可以然后sudo apt remove python3-pyqt5GUI 仍然可以工作,但是一旦我sudo apt autoremove停止工作。
  2. 显然(这在过去十分钟里对我来说是新的)我可以通过 pip 在 venv 中运行PyQT5 版本 5.14.1,而根本不需要安装 APT 版本(尽管看起来 Ubuntu 20.04 有一个 pre -安装的版本也是 5.14.1)。

但是没有一组已安装的依赖项解决了这个问题,因为两者都存在额外的信号发射。


编辑以回应引用的评论:

执行以下操作并告诉我您在每个步骤中得到了什么: 1) 删除 python3-pyqt5

输出:

snackhole@UbuntuMachine:~$ sudo apt remove python3-pyqt5
[sudo] password for snackhole: 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libdouble-conversion3 libpcre2-16-0 libqt5core5a libqt5dbus5 libqt5designer5
  libqt5gui5 libqt5help5 libqt5network5 libqt5printsupport5 libqt5sql5
  libqt5sql5-sqlite libqt5svg5 libqt5test5 libqt5widgets5 libqt5xml5
  libxcb-xinerama0 libxcb-xinput0 python3-sip qt5-gtk-platformtheme
  qttranslations5-l10n
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
  python3-pyqt5
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 16.4 MB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 216860 files and directories currently installed.)
Removing python3-pyqt5 (5.14.1+dfsg-3build1) ...
snackhole@UbuntuMachine:~$ sudo apt autoremove
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be REMOVED:
  libdouble-conversion3 libpcre2-16-0 libqt5core5a libqt5dbus5 libqt5designer5
  libqt5gui5 libqt5help5 libqt5network5 libqt5printsupport5 libqt5sql5
  libqt5sql5-sqlite libqt5svg5 libqt5test5 libqt5widgets5 libqt5xml5
  libxcb-xinerama0 libxcb-xinput0 python3-sip qt5-gtk-platformtheme
  qttranslations5-l10n
0 upgraded, 0 newly installed, 20 to remove and 0 not upgraded.
After this operation, 53.0 MB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 216792 files and directories currently installed.)
Removing qt5-gtk-platformtheme:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5designer5:amd64 (5.12.8-0ubuntu1) ...
Removing libqt5xml5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5svg5:amd64 (5.12.8-0ubuntu1) ...
Removing libqt5test5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5printsupport5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5help5:amd64 (5.12.8-0ubuntu1) ...
Removing libqt5sql5-sqlite:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5sql5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing python3-sip (4.19.21+dfsg-1build1) ...
Removing qttranslations5-l10n (5.12.8-0ubuntu1) ...
Removing libqt5widgets5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5gui5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5network5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libqt5dbus5:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libxcb-xinerama0:amd64 (1.14-2) ...
Removing libxcb-xinput0:amd64 (1.14-2) ...
Removing libqt5core5a:amd64 (5.12.8+dfsg-0ubuntu1) ...
Removing libdouble-conversion3:amd64 (3.1.5-4ubuntu1) ...
Removing libpcre2-16-0:amd64 (10.34-7) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...
snackhole@UbuntuMachine:~$ 

  1. 创建一个virtualenv并激活它

完成,在一个新目录中,虽然我习惯于通过 PyCharm 创建虚拟环境,所以我必须先运行sudo apt install python3-venv


  1. 更新 pip 并安装 PyQt5:python -m pip install --upgrade pip 和 python -m pip install pyqt5。

输出:

(venv) snackhole@UbuntuMachine:~/Documents/Test$ python3 -m pip install --upgrade pip
Collecting pip
  Using cached pip-20.1.1-py2.py3-none-any.whl (1.5 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 20.0.2
    Uninstalling pip-20.0.2:
      Successfully uninstalled pip-20.0.2
Successfully installed pip-20.1.1
(venv) snackhole@UbuntuMachine:~/Documents/Test$ python3 -m pip install pyqt5
Collecting pyqt5
  Using cached PyQt5-5.15.0-5.15.0-cp35.cp36.cp37.cp38-abi3-manylinux2014_x86_64.whl (76.6 MB)
Collecting PyQt5-sip<13,>=12.8
  Using cached PyQt5_sip-12.8.0-cp38-cp38-manylinux1_x86_64.whl (293 kB)
Installing collected packages: PyQt5-sip, pyqt5
Successfully installed PyQt5-sip-12.8.0 pyqt5-5.15.0

  1. 使用以下命令运行您的脚本 QT_DEBUG_PLUGINS=1 python your_script.py – eyllanesc

输出:

(venv) snackhole@UbuntuMachine:~/Documents/Test$ QT_DEBUG_PLUGINS=1 python3 QTextEditBug.py
QFactoryLoader::QFactoryLoader() checking directory path "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms" ...
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqeglfs.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqeglfs.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "eglfs"
        ]
    },
    "archreq": 0,
    "className": "QEglFSIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("eglfs")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqlinuxfb.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqlinuxfb.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "linuxfb"
        ]
    },
    "archreq": 0,
    "className": "QLinuxFbIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("linuxfb")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqminimal.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqminimal.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "minimal"
        ]
    },
    "archreq": 0,
    "className": "QMinimalIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("minimal")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqminimalegl.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqminimalegl.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "minimalegl"
        ]
    },
    "archreq": 0,
    "className": "QMinimalEglIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("minimalegl")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqoffscreen.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqoffscreen.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "offscreen"
        ]
    },
    "archreq": 0,
    "className": "QOffscreenIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("offscreen")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqvnc.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqvnc.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "vnc"
        ]
    },
    "archreq": 0,
    "className": "QVncIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("vnc")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-egl.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-egl.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "wayland-egl"
        ]
    },
    "archreq": 0,
    "className": "QWaylandEglPlatformIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("wayland-egl")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-generic.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-generic.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "wayland"
        ]
    },
    "archreq": 0,
    "className": "QWaylandIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("wayland")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-xcomposite-egl.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-xcomposite-egl.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "wayland-xcomposite-egl"
        ]
    },
    "archreq": 0,
    "className": "QWaylandXCompositeEglPlatformIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("wayland-xcomposite-egl")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-xcomposite-glx.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwayland-xcomposite-glx.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "wayland-xcomposite-glx"
        ]
    },
    "archreq": 0,
    "className": "QWaylandXCompositeGlxPlatformIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("wayland-xcomposite-glx")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwebgl.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqwebgl.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "webgl"
        ]
    },
    "archreq": 0,
    "className": "QWebGLIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("webgl")
QFactoryLoader::QFactoryLoader() looking at "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so"
Found metadata in lib /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "xcb"
        ]
    },
    "archreq": 0,
    "className": "QXcbIntegrationPlugin",
    "debug": false,
    "version": 331520
}


Got keys from plugin meta data ("xcb")
QFactoryLoader::QFactoryLoader() checking directory path "/usr/bin/platforms" ...
Cannot load library /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (libxcb-xinerama.so.0: cannot open shared object file: No such file or directory)
QLibraryPrivate::loadPlugin failed on "/home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so" : "Cannot load library /home/snackhole/Documents/Test/venv/lib/python3.8/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (libxcb-xinerama.so.0: cannot open shared object file: No such file or directory)"
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, webgl, xcb.

Aborted (core dumped)

编辑:sudo apt install libxcb-xinerama0按照@eyllanesc 的建议运行绕过了对 APT 安装的需要python3-pyqt5,这很好(合法地,不是讽刺!),但不幸的是实际上并没有解决提示我问题的问题:测试应用程序,在 venv 中运行安装了 PyQT5(无论是 5.15.0 还是 5.14.1),当焦点QTextEdit被设置为只读然后切换回可编辑后,仍然会发出这个令人费解的单个额外信号。


编辑:奇怪的是,当您离开时,测试应用程序也会触发五个 textChanged信号。单独按 alt 会产生一个信号,然后当窗口失去焦点时,它会再产生四个信号……在 Windows 10 上都不会发生。

标签: pythonubuntupyqt5

解决方案


推荐阅读