[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