[PyQt] pyqt5 segfault when passing callback functions
Ben
bugreporter at vescent.com
Mon Nov 3 00:32:04 GMT 2014
I'm trying to pass a callback function from QML/JS to python and back
again. (Basically, JS tells python to do X and run this (JS) function
when X returns). Unfortunately, this causes segfaults. I'm using pyqt5
with either python 2.7 or 3.4 with the same results. Tweaking the code
(adding / removing log lines, etc) can change the behavior so I'm
thinking that some garbage collection on the QT/QML/JS side is deleting
the function from memory, but python is keeping the (now invalid)
function pointer and executing it, with inconsistent results. I can't
find any example code for doing this with pyqt5/QML (we have a c++ qt5
implementation that works) but seems like a common thing to do. Is there
a better approach? Or just something dumb I'm doing?
I wrote a very simple program that segfaults and shows my problem. It is
also available on https://github.com/ben-github/pyqt5-segfault and I'll
also show it inline below. When I run the code I get:
Starting ICE Control GUI...
qml: onLoad start==================
Enqueuing function of #version 1
Callback is <PyQt5.QtQml.QJSValue object at 0x7f213dd05278>
Callback is callable <built-in method isCallable of QJSValue object at
0x7f213dd05278>
Callback is callable False
qml: =========================
processing responses
Callback is <PyQt5.QtQml.QJSValue object at 0x7f213dd05278>
Callback is callable <built-in method isCallable of QJSValue object at
0x7f213dd05278>
Segmentation fault
The segfault is from the line in main.py:
print('Callback is callable %s' % self.callback.isCallable())
where self.callback is the JS callback function.
Here's my program:
Main.py:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
class iceController(QObject):
def __init__(self):
QObject.__init__(self)
self.callback = None
def dump(self):
print('Callback is %s' % self.callback)
print('Callback is callable %s' % self.callback.isCallable)
print('Callback is callable %s' % self.callback.isCallable())
self.callback.call()
@pyqtSlot(str, 'QJSValue')
def enqueue(self, command, callback):
print('Enqueuing function of %s' % command)
self.callback = callback
self.dump()
@pyqtSlot()
def processResponses(self):
print('processing responses')
self.dump()
@pyqtSlot(str)
def log(self, s):
print(s)
print('Starting ICE Control GUI...')
app = QApplication(sys.argv)
view = QQuickView()
context = view.rootContext()
ice = iceController()
context.setContextProperty('PyConsole', ice)
context.setContextProperty('ice', ice)
view.setSource(QUrl("main.qml"))
main.qml file is:
import QtQuick 2.0
import "application.js" as App
Rectangle {
id: appWindow
width: 825
height: 600
color: '#000000'
Component.onCompleted: App.onLoad()
}
and finally, application.js is:
function queueMe(response)
{
console.log('Enqueued Function Called: ' + response + "\n");
}
function onLoad()
{
console.log("onLoad start==================")
ice.enqueue('#version 1', 1, queueMe);
console.log(" =========================")
ice.processResponses();
console.log("onLoad done==================")
}
Any suggestions welcome as my coworkers and I are totally stuck on what
could be the problem. Thanks!
More information about the PyQt
mailing list