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
The importNames and importNames_sync adopt the protocol of importModule.
The importNames(module_name, [objects_names], callback) is asynchronous.
It calls the synchronous importNames_sync(module_name, [object_names]) via the signal import_names.
importNames_sync imports the module (with PyImport_ImportModule(moduleName)) and loops over the given names
trying to get the corresponding object from the module (by PyObject_GetAttrString).
On success the object is inserted into the global space of the Python interpreter.
(priv->globals.borrow() is the pointer to the corresponding Python dict.)
Each failed import separately emits the error message and continues to the next iteration.
Changes to be committed:
modified: src/qpython.cpp
modified: src/qpython.h
modified: src/qpython_worker.cpp
modified: src/qpython_worker.h
If sourceSize is not set at all, use defaultSize() as returned
by the QSvgRenderer instance.
If only width or height is set, get the other value from defaultSize()
and scale it according to the set one to keep aspect ratio of the image.
The image provider can already render SVG images supplied
as format_data due to using QImage as backend.
This unfortunately only works correctly as long as the SVG image is
not scaled, as the image provider ignores the requested_size parameter
for format_data, causing the SVG image to be first rendered at its default size
and then scaled at the bitmap level, resulting in a very blurry image.
As there does not appear to be any easy fix for this when working with
the format_data type add a new data type called format_svg_data that
properly renders the SVG image at requested size with QtSvgRenderer.
Also the documentation has been updated to include format_svg_data
and an example has been added.
In some cases (e.g. release builds, see issue #46) it could be
that pyobject_converter.h is included in more than one compile
unit, which also creates two instances of the PyDateTimeAPI
static variable, defined in Python's datetime.h. For this reason,
we must not keep track of the initialization state manually, but
instead just initialize PyDateTimeAPI whenever it is NULL.
Calling QJSValue::toVariant() can cause QJSValue to call into QML engine. Since
we perform this correction from QPythonWriter thread context, we end up calling
QML engine from non-GUI thread and causing race conditions and crashes.
This change performs the initial unboxing in QPython::call() and passes to
QPythonWriter the actual value of QJSValue in QVariant.
This fixes issue #36.