crash - PySide crashing Python when emitting None between threads -
[edit] not pure duplicate of pyside emit signal causes python crash question. question relates (now) known bug in pyside preventing none being passed across threads. other question relates hooking signals spinner box. i've updated title of question better reflect problem facing. [/edit]
i've banged head against situation pyside behaves subtly different pyqt. well, subtly pyside crashes python whereas pyqt works expect.
i'm new pyside , still new pyqt maybe i'm making basic mistake, damned if can figure out... hoping 1 of fine folks can give pointers!
the full app batch processing tool , cumbersome describe here, i've stripped problem down bare essentials in code-sample below:
import threading try: # raise importerror() # uncomment line show pyqt works correctly pyside import qtcore, qtgui except importerror: 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 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_()
running displays window "do it" button. clicking crashes python if running under pyside. uncomment importerror
on line 4 see pyqt* correctly run dummy function. or uncomment return
statement on line 20 see pyside correctly run.
i don't understand why emitting none
makes python/pyside fail badly?
the goal offload processing (whatever dummy does) thread, keeping main gui thread responsive. again has worked fine pyqt not pyside.
any , advice super appreciated.
this 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) >>> pyqt4 import qt >>> qt.qversion() '4.8.2'
so, if argument pyside neglected , bug, might come workaround, right?
by introducing sentinel replace none, , emitting it problem can circumvented, sentinel has swapped none in callbacks , problem bypassed.
good grief though. i'll post code i've ended invite further comments, if got better alternatives or actual solutions give shout. in meantime guess this'll do:
_pyside_none_sentinel = object() def pyside_none_wrap(var): """none -> sentinel. wrap around out-of-thread emitting.""" if var none: return _pyside_none_sentinel return var def pyside_none_deco(func): """sentinel -> none. decorate callbacks react out-of-thread signal emitting. modifies function such sentinels passed in transformed none. """ def sentinel_guard(arg): if arg _pyside_none_sentinel: return none return arg def inner(*args, **kwargs): newargs = map(sentinel_guard, args) newkwargs = {k: sentinel_guard(v) k, v in kwargs.iteritems()} return func(*newargs, **newkwargs) return inner
modifying original code arrive @ 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'll win awards, seem fix issue.
Comments
Post a Comment