为啥我的 pyqt 信号错误会冻结 ui,直到调用另一个 python 函数

Posted

技术标签:

【中文标题】为啥我的 pyqt 信号错误会冻结 ui,直到调用另一个 python 函数【英文标题】:Why do my errors from pyqt signals freeze the ui until another python function gets called为什么我的 pyqt 信号错误会冻结 ui,直到调用另一个 python 函数 【发布时间】:2019-01-14 22:05:38 【问题描述】:

在 Maya 2016 中一切正常。但是在 Maya 2017 中(在我完成所有 pyqt 转换之后),从 qt 信号调用的函数中发生的任何错误都不会立即显示。然后如果我在脚本编辑器中运行另一个代码,例如“print 0”,就会出现上一个函数的错误

要试一试,只需运行代码,然后单击“错误”按钮。

我在 Windows 和 Mac 上尝试过。还请朋友试穿 - 他也有同样的问题。同样,在 Maya 2016 上一切正常。该问题仅在 Maya 2017 中发生

from PySide2 import QtWidgets, QtGui, QtCore
from shiboken2 import wrapInstance
import maya.OpenMayaUI as mui


mainWin = None

def showUI():
    global mainWin
    if mainWin != None:
        print "reloading UI..."
        mainWin.close()
    mainWin = myWindow()
    mainWin.show()


def getMayaWindow():
    ptr = mui.MQtUtil.mainWindow()
    return wrapInstance(long(ptr), QtWidgets.QWidget)


class myWindow(QtWidgets.QDialog):

    def __init__(self, parent=getMayaWindow()):

        super(myWindow, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)

        def funcApply():
            oooo # this should error!

        self.layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.TopToBottom, self)
        self.testButton = QtWidgets.QPushButton('error')
        self.layout.addWidget(self.testButton)
        self.testButton.clicked.connect(funcApply)

showUI()

【问题讨论】:

只是猜测;但可能是从 Qt 4 转移到 Qt 5 时引入的一个错误……您是否也在 2018 年看到它?刚在2018.5测试了一下,马上报错# NameError: global name 'oooo' is not defined 无法在 Linux 上的 Maya 2018 中复制该问题 我也相信这是从 Qt4 到 Qt5 的转变带来的一个错误 - 但让我感到困惑的是,我是唯一一个遇到这个问题的人。因为这个 UI 的代码比较简单。感谢您使用 maya2018 对其进行测试-也许我将不得不跳过 2017 年并转到 2018 年.. 哇,2017 年在 Linux 上确认。如果我找到任何解决方案,我会报告。这是一个非常讨厌的错误,他们怎么能放过这个!这也是 Maya 2017 Update 2 的一个错误。 【参考方案1】:

就像我在 cmets 中所说,我可以在 2017 年和 2017 年更新 2 中复制您的错误,但在 2018 年工作得非常好。

幸运的是,有一个足够简单的解决方案,您只需将方法移出__init__ 即可使其成为普通的实例方法。现在按预期执行此操作会出错。 (老实说,在__init__ 中有一个嵌套函数有点奇怪,不管它是否有问题,我都会移动它)

有趣的是,静态方法也有同样的问题!如果你试图在课堂上给他们打电话,他们会挂掉。但是,如果你从类外部调用它,它可以正常工作,或者如果你用 try except 包装它,它现在可以在从类内部调用时工作。

查看以下代码。我包括了3个案例,一个实例化的方法,一个静态的方法,一个用try包裹的静态方法except

from PySide2 import QtWidgets, QtGui, QtCore
from shiboken2 import wrapInstance
import maya.OpenMayaUI as mui


mainWin = None


def showUI():
    global mainWin
    if mainWin != None:
        print "reloading UI..."
        mainWin.close()
    mainWin = myWindow()
    mainWin.show()


def getMayaWindow():
    ptr = mui.MQtUtil.mainWindow()
    return wrapInstance(long(ptr), QtWidgets.QWidget)


class myWindow(QtWidgets.QDialog):

    def __init__(self, parent=getMayaWindow()):

        super(myWindow, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)

        # Works.
        self.testButton = QtWidgets.QPushButton('error')
        self.testButton.clicked.connect(self.funcApply)

        # Fails.
        self.testButton2 = QtWidgets.QPushButton('error static method')
        self.testButton2.clicked.connect(self.staticFuncApply)

        # Works.
        self.testButton3 = QtWidgets.QPushButton('error static method with try/except')
        self.testButton3.clicked.connect(self.staticTryExceptFuncApply)

        self.layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.TopToBottom, self)
        self.layout.addWidget(self.testButton)
        self.layout.addWidget(self.testButton2)
        self.layout.addWidget(self.testButton3)
        self.setLayout(self.layout)  # Normally need to specify what layout this widget needs to use.

    # Move this out and make it a normal instanced method.
    # This will work as expected.
    def funcApply(self):
        oooo # this should error!

    # Static methods still broken, but ok in Maya 2018.
    @staticmethod
    def staticFuncApply():
        oooo

    # Static methods wrapped with try except works.
    @staticmethod
    def staticTryExceptFuncApply():
        try:
            oooo
        except Exception as e:
            print e


showUI()

# myWindow.staticFuncApply()  # Calling static method outside of class works ok.

所有这些都在 Maya 2018 中工作,但静态方法在 2017 年将失败。 希望您可以避免使用静态方法,因为将其全部包装在 try except 中并不是一个实用的解决方案。您也可以尝试将您的 Maya 更新到 2017 更新 4 以查看它是否已修补。

希望这会有所帮助!

【讨论】:

是的,就是这样 - 感谢您发现它!转换我的所有代码会很棘手,因为在某些情况下,我会在 for 循环中按程序创建函数 - 但我相信我会找到一种方法。至于 try-except 语句,我也注意到他正确地捕捉到了它们。但是如果你在 except 语句中提出错误,你就会遇到同样的问题 也许对于简单的嵌套函数,您可以将它们替换为lambda?嗯,很高兴你现在至少找到了一个出发点。或者说服你的雇主切换到 2018 年:P 不,lambda 也有同样的问题。但我会想办法的。只需要改变一些事情。 最终只是在按钮上创建了动态属性,然后创建了我自己的继承 QPushButton 的按钮类。并且,mouseReleaseEvent() 具有通常单击信号函数会执行的魔力。所以一切又好了。

以上是关于为啥我的 pyqt 信号错误会冻结 ui,直到调用另一个 python 函数的主要内容,如果未能解决你的问题,请参考以下文章

为啥 PHP 错误会打印两次?

PyQt 4 UI 冻结

pyqt4中的线程

为啥我的 AsyncTask 会冻结我的 UI 线程?

为啥密码错误会导致“填充无效,无法删除”?

为啥这个错误会出现在命令提示符中?