【问题标题】:PyQt modules can't be imported after QtWidgets.QApplication(sys.argv)在 QtWidgets.QApplication(sys.argv) 之后无法导入 PyQt 模块
【发布时间】:2017-05-01 12:52:23
【问题描述】:

概览

我在行后导入模块时遇到问题 QtWidgets.QApplication(sys.argv),假设我得到了这个小sn-p main.py

import sys
import importlib
from PyQt5 import QtWidgets

print('Sys Path:')
print('  %s\n' % '\n  '.join(sys.path))

if sys.argv[-1] == '1':
    print('Importing Before...\n')
    from PyQt5 import Qt
    app = QtWidgets.QApplication(sys.argv)

elif sys.argv[-1] == '2':
    print('Importing After...\n')
    app = QtWidgets.QApplication(sys.argv)
    from PyQt5 import Qt

print('Done')
  • 如果我运行 python main.py 1,一切都会按预期运行。
  • 如果我运行python main.py 2,进程会挂起(可能处于无限循环中)而不会出现任何错误。

python main.py 2的输出:

(py352) D:\sources\personal\python\pyqt\mcve>python main.py 2           
Sys Path:                                                               
  D:\sources\personal\python\pyqt\mcve                                  
  D:\sources\personal\python                                            
  d:\virtual_envs\py352\Scripts\python35.zip                            
  d:\virtual_envs\py352\DLLs                                            
  d:\virtual_envs\py352\lib                                             
  d:\virtual_envs\py352\Scripts                                         
  c:\Python352\Lib                                                      
  c:\Python352\DLLs                                                     
  d:\virtual_envs\py352                                                 
  d:\virtual_envs\py352\lib\site-packages                               

Importing After...                                                      
(HANG)

尝试

在 win7 上用几个 virtualenvs 测试:

  • Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32 win7
  • Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32

Pyqt 是使用 pip 安装在 virtualenvs 上的,版本如下:

>>> QtCore.QT_VERSION
329472
>>> QtCore.QT_VERSION_STR
'5.7.0'
>>> QtCore.PYQT_VERSION_STR
'5.7'

相关信息

#pyqt freenode 频道的一些非常好的人帮助我测试了 repo,但他们都无法重现问题,他们使用的 python 版本和平台是:

  • win10 - 3.5.2 |Anaconda 4.1.1(64 位)
  • win8 - 3.5.2(v3.5.2:4def2a2901a5,2016 年 6 月 25 日,22:01:18)
  • ubuntu 16.04 - 3.5.2(默认,2016 年 11 月 17 日,17:05:23)

问题

  • from PyQt5 import Qt(或其他 pyqt 模块)卡在我的盒子上而其他人无法重现的原因是什么?
  • 如何解决此问题?这对我来说很重要,因为我想在我的 pyqt 应用程序启动后动态加载插件

【问题讨论】:

  • 一个具体建议:创建一个测试脚本,在创建QApplication 之前和之后简单地尝试执行importlib.import_module(mod_name)。如果它仍然挂起,你有你的 mcve;如果它没有挂起,您肯定会知道您还没有足够地调试您的测试用例。应该可以将list_plugins 重构为独立函数。然后,您可以将它指向包含*.py 文件的任何目录,看看它是否仍然挂起。这个想法是只隔离一小部分导致问题的代码并消除所有内容
  • 你能试试我在之前的 cmets 中提出的两个建议吗?它应该只需要大约十分钟的工作时间,它将允许您发布适当的 mcve。其他内容可能无关紧要 - 这几乎可以肯定是特定于您的特定设置的问题。
  • @ekhumoro 好的,我再次更新了 repo,如果您有时间,请告诉我您是否发现了可以进一步改进的其他内容,以进一步降低复杂性。
  • 您能否将代码放入问题中,以便每个人都可以看到您实际测试的内容?如果您遵循我之前的建议,您应该只得到一个小文件。此外,您需要清楚地说明运行测试时会发生什么,因为您目前是唯一可以重现问题的人。
  • @ekhumoro 好的,我听从了你的建议,结果现在人们可以在这个问题中看到 repo 的内容,或者如果他们发现它是一种更快的方式来克隆 repo 内容一枪。我无法进一步简化测试用例,你可以看到结果是一组 5 个文件(不知道你为什么在我最终只得到一个小文件之前说),我仍然不确定将代码放在问题中而不是 git clone... 有什么好处...但让我们试一试,因为这个问题已经困扰我好几天了。

标签: python windows-7 python-3.5 pyqt5 python-import


【解决方案1】:

在导入库后在代码中使用这些行会有所帮助。

from PyQt5 import QtWidgets
app = QtWidgets.QApplication.instance()
if app is not None:
    import sip
    app.quit()
    sip.delete(app)
import sys
from PyQt5 import QtCore, QtWebEngineWidgets
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
app = QtWidgets.qApp = QtWidgets.QApplication(sys.argv)

【讨论】:

  • 虽然此代码可以回答问题,但提供有关 如何 和/或 为什么 解决问题的附加上下文将改善答案的长期价值。
【解决方案2】:

这(还)不是一个真正的答案,但它可能提供了如何找到答案的第一步。

以下是我在对该问题的第一条评论中建议的最小测试用例。它只测试一件事:在创建QApplication 之后调用importlib.import_module 会使解释器挂在您的系统上吗?请注意,目前,它仅尝试从 python 标准库中导入模块。一步一步进行,并小心避免引入任何潜在的混淆变量,这一点至关重要。

完全按照下面的描述运行此脚本,并将输出添加到您的问题中。 (即使选项 2 没有挂起,sys.path 的详细信息也可能是相关的)。

import sys, importlib
from PyQt5 import QtWidgets

print('Sys Path:')
print('  %s\n' % '\n  '.join(sys.path))

mod = None
modname = 'collections.abc'
# modname = 'PyQt5.Qt'

if sys.argv[-1] == '1':
    print('Importing Before...\n')
    mod = importlib.import_module(modname)
    app = QtWidgets.QApplication(sys.argv)

elif sys.argv[-1] == '2':
    print('Importing After...\n')
    app = QtWidgets.QApplication(sys.argv)
    mod = importlib.import_module(modname)
    # from PyQt5 import Qt

print('Result: %r' % mod)

像这样运行脚本:

$ python /tmp/test.py 1

然后像这样:

$ python /tmp/test.py 2

在我的系统(ArchLinux、Python-3.5.2、Qt-5.7.1、PyQt-5.7)上,第二个产生以下输出:

Sys Path:
  /tmp
  /usr/lib/python35.zip
  /usr/lib/python3.5
  /usr/lib/python3.5/plat-linux
  /usr/lib/python3.5/lib-dynload
  /usr/lib/python3.5/site-packages

Importing After...

Result: <module 'collections.abc' from '/usr/lib/python3.5/collections/abc.py'>

更新

第一步确定importlib 本身不是问题的原因。第二步是确定哪个特定的导入模块是问题的根源。

我在测试脚本中添加了两行(注释),这将允许这样做。第一个检查导入PyQt5.Qt 是否会触发挂起。如果是,则第二个检查正常的import 语句是否也会触发挂起。

请注意,from PyQt5 import Qt 有效地导入了所有内容,包括一些非常重量级且可能很麻烦的模块,例如 QtWebEngineWidgets。因此有必要进一步细化导入以正确识别问题的确切根源。

更新 2

QtWebEngineWidgets 是众所周知的问题来源,通常需要小心处理。以下解释器会话输出似乎与您当前的问题有关:

>>> from PyQt5 import QtWidgets, QtCore
>>> app = QtWidgets.QApplication([''])
>>> from PyQt5 import QtWebEngineWidgets
Qt WebEngine seems to be initialized from a plugin. Please set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute before constructing QGuiApplication.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: QtWebEngineWidgets must be imported before a QCoreApplication instance is created
>>>
>>> QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
>>> from PyQt5 import QtWebEngineWidgets
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: QtWebEngineWidgets must be imported before a QCoreApplication instance is created

这都是正常的行为(尽管目前我找不到任何官方文档)。但是让我们使用Qt 模块尝试同样的事情:

>>> from PyQt5 import QtWidgets, QtCore
>>> QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
>>> app = QtWidgets.QApplication([''])
>>> from PyQt5 import Qt
>>> Qt.QWeb
Qt.QWebChannel(                      Qt.QWebEngineUrlRequestInterceptor(  Qt.QWebHitTestResult(                Qt.QWebSocketCorsAuthenticator(
Qt.QWebChannelAbstractTransport(     Qt.QWebEngineUrlRequestJob(          Qt.QWebInspector(                    Qt.QWebSocketProtocol(
Qt.QWebDatabase(                     Qt.QWebEngineUrlSchemeHandler(       Qt.QWebPage(                         Qt.QWebSocketServer(
Qt.QWebElement(                      Qt.QWebFrame(                        Qt.QWebPluginFactory(                Qt.QWebView(
Qt.QWebElementCollection(            Qt.QWebHistory(                      Qt.QWebSecurityOrigin(
Qt.QWebEngineCookieStore(            Qt.QWebHistoryInterface(             Qt.QWebSettings(
Qt.QWebEngineUrlRequestInfo(         Qt.QWebHistoryItem(                  Qt.QWebSocket(
>

因此,当Qt 模块被导入之后 QApplication 创建时,它看起来有特殊处理 - 尽管一些 QWebEngine 类可用,其中大部分已被省略(例如QWebEngineViewQWebEnginePage 等)。但似乎在您的特定设置中,这可能无法正常工作。如果是这样,您可能不得不向 PyQt 的作者提出这个问题,因为这可能需要了解 Qt 模块的内部工作原理。

【讨论】:

  • 首先,感谢您第一次尝试的帮助,希望这会激励其他人提出一些新的想法。我必须说你将 {main_ok.py, main_bug.py} 合并到 test.py 中是个好主意,稍后我将更新 repo 和问题以简化它。不幸的是,您的测试不会在我的盒子上显示任何问题(没有挂起)。你会在我的回购的最后一次提交中看到的一件事是文件gui\operator_object.py 包含from PyQt5 import Qt...但在我得到from PyQt5.Qt import qDrawShadeRect 之前,我已经意识到它们都在我的盒子上产生了挂起。
  • 另外,一个相关的细节,如果我将gui\operator_object.py 中的from PyQt5 import Qt 替换为import collections.abc,我将不会遇到问题。因此,出于某种原因,PyQt 模块似乎允许我在这里重现该错误。 +1 为您的时间和帮助顺便说一句;D
  • @BPL。所以现在我已经向你展示了如何正确地测试事情,你应该能够自己做剩下的事情。如果你一步一步地进行,仔细检查每个元素,你最终会找到问题的真正根源。如果您更改了问题的标题和大部分内容会很有帮助,因为我现在已经毫无疑问地确定import_module 不会本身导致问题。
  • @BPL。我冒昧地将下一步添加到我的答案中。请尝试新的测试并确认它们是否会重现问题。如果他们这样做,那应该完全消除 import_lib 和插件作为原因。如果您可以编辑您的问题并说明您正在测试的 Qt5 和 PyQt5 的确切版本以及您如何安装它们(即它们来自二进制安装程序、轮子、从源代码编译等),这也会很有帮助。跨度>
  • 好的,多亏了你,问题已被简化为广告上限,我们对问题进行了更多限制。如果我一开始就知道这是真正的问题,我会直接在问题中提供这种类型的mcve。那么,您认为下一步应该如何解决这个问题?另外,您认为我首先延迟了真正 mcve 的创建是什么错误?由于某种原因,我一直认为 importlib 有问题,而且我浪费了很多时间。我问这个是因为我下次想提高我的调试技能
猜你喜欢
  • 2018-02-10
  • 2018-10-21
  • 2012-07-27
  • 2013-10-25
  • 2017-03-05
  • 2017-07-08
  • 1970-01-01
  • 1970-01-01
  • 2012-11-06
相关资源
最近更新 更多