PySide Python crash when emitting No between threads

[edit] This is not a pure duplicate of the PySide signal , which causes python to crash . This question relates specifically to the (now) known bug in PySide that prevents None threading . Another question is connected with the connection of signals to the box with spinners. I updated the title of this question to better reflect the problem I am having. [/ edit]

I hit my head about a situation where PySide behaves differently from PyQt. Well, I speak subtly, but in fact PySide produces Python, whereas PyQt works as I expect.

Under PySide my app crashes

I am completely new to PySide and still pretty new to PyQt, so maybe I am making some kind of basic mistake, but damned if I can figure it out ... really hope one of you great people can give some pointers !

The complete application is a batch processing tool and too cumbersome to describe it here, but I decided that the problem in its example is below:

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_() 

Launching this window displays a window with a "do it" button. When you click on it, Python crashes when working under PySide. Uncomment ImportError on line 4 to see PyQt * run the Dummy function correctly. Or uncomment the return on line 20 to see PySide start correctly.

I don't understand why emitting None makes Python / PySide fail so badly?

The goal is to offload processing (no matter what Dummy does) to another thread, while preserving the main GUI thread. Again, this did a great job with PyQt, but obviously not much with PySide.

All tips will be appreciated.

It is under:

  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' 
+6
source share
1 answer

So, if the argument is that PySide is ignored, and this is really a mistake, we could also find a workaround, right?

By entering the sentry to replace None, and releasing it, the problem can be circumvented, then the sentinel must be replaced back by None in callbacks and the problem is bypassed.

Good grief. I will send the code I received to invite further comments, but if you have better alternatives or relevant solutions, then give out a cry. In the meantime, I think this will do:

 _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 

By modifying the source code, we come to this solution:

 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_()) 

I doubt that I will win any awards, but it seems to fix the problem.

+2
source

Source: https://habr.com/ru/post/969483/


All Articles