线程之间不发出任何消息时PySide崩溃Python

线程之间不发出任何消息时PySide崩溃Python,python,crash,pyqt,pyside,Python,Crash,Pyqt,Pyside,[编辑]这不是问题的完全重复。这个问题特别涉及(现在)。另一个问题涉及将信号连接到微调器盒。我更新了这个问题的标题,以更好地反映我所面临的问题[/edit] 我的头撞到了PySide与PyQt行为微妙不同的情况。好吧,我说得很微妙,但实际上PySide崩溃了Python,而PyQt却如我所料工作 我对PySide完全陌生,对PyQt也相当陌生,所以也许我犯了一些基本的错误,但如果我能弄明白的话,那就见鬼了。。。真的希望你们中的一个好人能给点建议 完整的应用程序是一个批处理工具,在这里描述起来太

[编辑]这不是问题的完全重复。这个问题特别涉及(现在)。另一个问题涉及将信号连接到微调器盒。我更新了这个问题的标题,以更好地反映我所面临的问题[/edit]

我的头撞到了PySide与PyQt行为微妙不同的情况。好吧,我说得很微妙,但实际上PySide崩溃了Python,而PyQt却如我所料工作

我对PySide完全陌生,对PyQt也相当陌生,所以也许我犯了一些基本的错误,但如果我能弄明白的话,那就见鬼了。。。真的希望你们中的一个好人能给点建议

完整的应用程序是一个批处理工具,在这里描述起来太麻烦了,但我在下面的代码示例中将问题归结为它的基本要素:

import threading

try:
    # raise ImportError()  # Uncomment this line to show PyQt works correctly
    from PySide import QtCore, QtGui
except ImportError:
    from PyQt4 import QtCore, QtGui
    QtCore.Signal = QtCore.pyqtSignal
    QtCore.Slot = QtCore.pyqtSlot


class _ThreadsafeCallbackHelper(QtCore.QObject):
    finished = QtCore.Signal(object)


def Dummy():
    print "Ran Dummy"
    # return ''  # Uncomment this to show PySide *not* crashing
    return None


class BatchProcessingWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self, None)

        btn = QtGui.QPushButton('do it', self)
        btn.clicked.connect(lambda: self._BatchProcess())

    def _BatchProcess(self):
        def postbatch():
            pass
        helper = _ThreadsafeCallbackHelper()
        helper.finished.connect(postbatch)

        def cb():
            res = Dummy()
            helper.finished.emit(res)  # `None` crashes Python under PySide??!
        t = threading.Thread(target=cb)
        t.start()


if __name__ == '__main__':  # pragma: no cover
    app = QtGui.QApplication([])
    BatchProcessingWindow().show()
    app.exec_()
运行此操作将显示一个带有“DoIt”按钮的窗口。如果在PySide下运行,单击它会使Python崩溃。取消对第4行上的
ImportError
的注释,以查看PyQt*是否正确运行了伪函数。或者取消注释第20行的
return
语句,以查看PySide是否正确运行

我不明白为什么发出
None
会让Python/PySide失败得如此严重

目标是将处理(无论Dummy做什么)转移到另一个线程,保持主GUI线程的响应性。同样,这在PyQt中工作得很好,但在PySide中显然不是很好

我们将非常感谢您的任何建议

这是根据:

    Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32

    >>> import PySide
    >>> PySide.__version_info__
    (1, 1, 0, 'final', 1)

    >>> from PyQt4 import Qt
    >>> Qt.qVersion()
    '4.8.2'

所以,如果论点是PySide被忽略了,而这确实是一个bug,那么我们不妨想出一个解决方法,对吗

通过引入一个sentinel来替换None,并发出它,可以避免问题,然后在回调中将sentinel换回None,从而绕过问题

不过我很伤心。我将发布我最终得到的代码,以邀请更多的评论,但是如果你有更好的替代方案或实际的解决方案,那么一定要大喊一声。与此同时,我想这样做可以:

_PYSIDE_NONE_SENTINEL = object()


def pyside_none_wrap(var):
    """None -> sentinel. Wrap this around out-of-thread emitting."""
    if var is None:
        return _PYSIDE_NONE_SENTINEL
    return var


def pyside_none_deco(func):
    """sentinel -> None. Decorate callbacks that react to out-of-thread
    signal emitting.

    Modifies the function such that any sentinels passed in
    are transformed into None.
    """

    def sentinel_guard(arg):
        if arg is _PYSIDE_NONE_SENTINEL:
            return None
        return arg

    def inner(*args, **kwargs):
        newargs = map(sentinel_guard, args)
        newkwargs = {k: sentinel_guard(v) for k, v in kwargs.iteritems()}
        return func(*newargs, **newkwargs)

    return inner
通过修改我的原始代码,我们得到了以下解决方案:

class _ThreadsafeCallbackHelper(QtCore.QObject):
    finished = QtCore.Signal(object)


def Dummy():
    print "Ran Dummy"
    return None


def _BatchProcess():
    @pyside_none_deco
    def postbatch(result):
        print "Post batch result: %s" % result

    helper = _ThreadsafeCallbackHelper()
    helper.finished.connect(postbatch)

    def cb():
        res = Dummy()
        helper.finished.emit(pyside_none_wrap(res))

    t = threading.Thread(target=cb)
    t.start()


class BatchProcessingWindow(QtGui.QDialog):
    def __init__(self):
        super(BatchProcessingWindow, self).__init__(None)

        btn = QtGui.QPushButton('do it', self)
        btn.clicked.connect(_BatchProcess)


if __name__ == '__main__':  # pragma: no cover
    app = QtGui.QApplication([])
    window = BatchProcessingWindow()
    window.show()
    sys.exit(app.exec_())

我怀疑这是否会赢得任何奖项,但它似乎确实解决了这个问题。

我不确定这是否是该问题的重复。例如,发出包含
(None,)
的元组不会导致崩溃,但直接发出
None
会导致崩溃。这似乎是一个问题,
None
不被认为是
对象类型
,可能吧?不过,我本以为这会引发一个异常,而不是Python的硬崩溃。我的同事发现了这个PySide bug,它似乎正好解决了这个问题:>当从另一个PySide发出一个带有None参数的信号时,我不知何故在我的googlin中没有发现它。创建于2012年,所以。。嗯,好发现。我怀疑它会很快得到修复(PySide的开发似乎相当停滞)。我的建议是把所有东西包装起来,我是一个元组,发出信号,然后在另一边打开包装。或者坚持使用PyQt4:pYup,这是一个有效的建议。我在下面添加了一个类似的解决方法,如果回答自己的问题是一种糟糕的形式,我不知道,但除非出现其他问题,否则我将勾选此项。鼓励回答您自己的问题:)我还尝试要求重新打开此问题。这真的不是复制品……如果我三天前读过这篇文章就好了。我为之工作的公司和我本人非常感谢你。这正如预期的那样有效。也许有点黑客,但是PySide最近没有引起任何注意,这个bug可能会一直存在。