1
0
mirror of https://github.com/thp/pyotherside.git synced 2025-01-28 23:52:55 +08:00

Fix call_sync when used with parameters

Factor out the unboxing in call() to a separate method.

To handle direct calls to call_sync(), rename the implementation of
call_sync() to call_internal() and add a call_sync() stub that calls
call_internal().

call_internal() can now takes a boolean "unbox" parameter without
impacting the formal interfaces that call() and call_sync() provide to
QML users. If the "unbox" parameter is true, it unboxes the arguments
first. If false, it doesn't.

Now call_sync() can call call_internal() requesting unboxing. call() can
also do the unboxing itself, and then later process() (the slot for
switching call() to a different thread) can call call_internal()
requesting no unboxing.

Since we now have and must keep track of two versions of the parameters
(boxed vs. unboxed), I also rename these parameters to keep things
explicit and clear.

Here's what was going wrong before:

call() was unboxing QJSValue elements inside the argument list into
plain QVariants, and then calling (indirectly, to switch threads)
call_sync().

call_sync() did no such unboxing. Since call_sync() is exposed directly
as an entry point, this meant that priv->call(), which appears to expect
unboxed argument lists, was failing in this case.

There is a comment noting that call() should do the unboxing in the GUI
thread so should not defer it. So the unboxing has to happen in two
different places depending on whether the user called call() or
call_sync().

Fixes: #49
This commit is contained in:
Robie Basak 2017-01-03 21:07:43 +00:00
parent 77738b5a76
commit 260c9c275c
4 changed files with 47 additions and 16 deletions

View File

@ -298,15 +298,10 @@ QPython::evaluate(QString expr)
return convertPyObjectToQVariant(o.borrow()); return convertPyObjectToQVariant(o.borrow());
} }
void QVariantList
QPython::call(QVariant func, QVariant args, QJSValue callback) QPython::unboxArgList(QVariant &args)
{ {
QJSValue *cb = 0; // Unbox QJSValue from QVariant
if (!callback.isNull() && !callback.isUndefined() && callback.isCallable()) {
cb = new QJSValue(callback);
}
// Unbox QJSValue from QVariant, since QJSValue::toVariant() can cause calls into
// QML engine and we don't want that to happen from non-GUI thread
QVariantList vl = args.toList(); QVariantList vl = args.toList();
for (int i = 0, c = vl.count(); i < c; ++i) { for (int i = 0, c = vl.count(); i < c; ++i) {
QVariant &v = vl[i]; QVariant &v = vl[i];
@ -315,11 +310,31 @@ QPython::call(QVariant func, QVariant args, QJSValue callback)
v = v.value<QJSValue>().toVariant(); v = v.value<QJSValue>().toVariant();
} }
} }
emit process(func, vl, cb); return vl;
}
void
QPython::call(QVariant func, QVariant boxed_args, QJSValue callback)
{
QJSValue *cb = 0;
if (!callback.isNull() && !callback.isUndefined() && callback.isCallable()) {
cb = new QJSValue(callback);
}
// Unbox QJSValue from QVariant, since QJSValue::toVariant() can cause calls into
// QML engine and we don't want that to happen from non-GUI thread
QVariantList unboxed_args = unboxArgList(boxed_args);
emit process(func, unboxed_args, cb);
} }
QVariant QVariant
QPython::call_sync(QVariant func, QVariant args) QPython::call_sync(QVariant func, QVariant boxed_args)
{
return call_internal(func, boxed_args, true);
}
QVariant
QPython::call_internal(QVariant func, QVariant args, bool unbox)
{ {
ENSURE_GIL_STATE; ENSURE_GIL_STATE;
@ -348,8 +363,18 @@ QPython::call_sync(QVariant func, QVariant args)
return QVariant(); return QVariant();
} }
// Unbox QJSValue from QVariant if requested. QPython::call may have done
// this already, but call_sync is also exposed directly, so it does not
// happen in this case otherwise
QVariant args_unboxed;
if (unbox) {
args_unboxed = unboxArgList(args);
} else {
args_unboxed = args;
}
QVariant v; QVariant v;
QString errorMessage = priv->call(callable.borrow(), name, args, &v); QString errorMessage = priv->call(callable.borrow(), name, args_unboxed, &v);
if (!errorMessage.isNull()) { if (!errorMessage.isNull()) {
emitError(errorMessage); emitError(errorMessage);
} }

View File

@ -297,7 +297,11 @@ class QPython : public QObject {
* \result The return value of the Python call as Qt data type * \result The return value of the Python call as Qt data type
**/ **/
Q_INVOKABLE QVariant Q_INVOKABLE QVariant
call_sync(QVariant func, QVariant args=QVariantList()); call_sync(QVariant func, QVariant boxed_args=QVariantList());
QVariant
call_internal(QVariant func, QVariant boxed_args=QVariantList(),
bool unbox=true);
/** /**
* \brief Get an attribute value of a Python object synchronously * \brief Get an attribute value of a Python object synchronously
@ -361,7 +365,7 @@ class QPython : public QObject {
void error(QString traceback); void error(QString traceback);
/* For internal use only */ /* For internal use only */
void process(QVariant func, QVariant args, QJSValue *callback); void process(QVariant func, QVariant unboxed_args, QJSValue *callback);
void import(QString name, QJSValue *callback); void import(QString name, QJSValue *callback);
void import_names(QString name, QVariant args, QJSValue *callback); void import_names(QString name, QVariant args, QJSValue *callback);
@ -375,6 +379,8 @@ class QPython : public QObject {
void disconnectNotify(const QMetaMethod &signal); void disconnectNotify(const QMetaMethod &signal);
private: private:
QVariantList unboxArgList(QVariant &args);
static QPythonPriv *priv; static QPythonPriv *priv;
QPythonWorker *worker; QPythonWorker *worker;

View File

@ -32,9 +32,9 @@ QPythonWorker::~QPythonWorker()
} }
void void
QPythonWorker::process(QVariant func, QVariant args, QJSValue *callback) QPythonWorker::process(QVariant func, QVariant unboxed_args, QJSValue *callback)
{ {
QVariant result = qpython->call_sync(func, args); QVariant result = qpython->call_internal(func, unboxed_args, false);
if (callback) { if (callback) {
emit finished(result, callback); emit finished(result, callback);
} }

View File

@ -34,7 +34,7 @@ class QPythonWorker : public QObject {
~QPythonWorker(); ~QPythonWorker();
public slots: public slots:
void process(QVariant func, QVariant args, QJSValue *callback); void process(QVariant func, QVariant unboxed_args, QJSValue *callback);
void import(QString func, QJSValue *callback); void import(QString func, QJSValue *callback);
void import_names(QString func, QVariant args, QJSValue *callback); void import_names(QString func, QVariant args, QJSValue *callback);