Giter Site home page Giter Site logo

thp / pyotherside Goto Github PK

View Code? Open in Web Editor NEW
361.0 361.0 49.0 670 KB

Python Bindings for Qt 5 and Qt 6. Allows you to access a CPython 3 interpreter directly from your Qt QML user interface code.

Home Page: https://thp.io/2011/pyotherside/

License: Other

Python 2.64% QML 10.15% C++ 82.92% C 1.60% QMake 2.22% Shell 0.47%
python3 qt6

pyotherside's People

Contributors

apollo13 avatar basak avatar blabber avatar danvratil avatar fkrull avatar greenaddress avatar h3xx avatar heirecka avatar m4rtink avatar martyone avatar thp avatar timegrid avatar uglide avatar vstinner avatar waldyrious avatar xealits avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyotherside's Issues

Crash when launching app using QQmlApplicationEngine from PyQt5

I get a segmentation fault if I launch a QML application that makes use of the Python component by using QQmlApplicationEngine from a PyQt5 application. The same does not happen if I launch it from the equivalent C++ application.

Minimal test case:

test.py - The Python launcher application:

from sys import argv, exit

from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine

app = None

def main():
    global app

    app = QGuiApplication(argv)

    engine = QQmlApplicationEngine()
    engine.load('test.qml')

    exit(app.exec_())

if __name__ == '__main__':
    main()

test.cpp - Same as above, but this time in C++:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("test.qml")));

    return app.exec();
}

test.pro - To build main.cpp:

TEMPLATE = app
QT += qml quick
SOURCES += test.cpp

test.qml - The test QML application:

import QtQuick 2.4
import QtQuick.Window 2.2

import io.thp.pyotherside 1.4

Window {
    width: 500
    height: 500
    visible: true

    Python {
    }
}

Launching using C++ ⇒ OK

This works fine, the window is shown and there's no crash:

estan@newton:~/test$ qmake
estan@newton:~/test$ make
g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtQuick -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtQml -isystem /usr/include/x86_64-linux-gnu/qt5/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++-64 -o test.o test.cpp
g++ -m64 -Wl,-O1 -o test test.o   -L/usr/X11R6/lib64 -lQt5Quick -lQt5Gui -lQt5Qml -lQt5Network -lQt5Core -lGL -lpthread 
estan@newton:~/test$ ./test 
estan@newton:~/test$

Launching using Python ⇒ CRASH

This gives a segmentation fault:

estan@newton:~/test$ python3 test.py 
Segmentation fault (core dumped)
estan@newton:~/test$

The backtrace is:

estan@newton:~/test$ gdb python3
GNU gdb (Ubuntu 7.10-1ubuntu2) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python3...Reading symbols from /usr/lib/debug//usr/bin/python3.4m...done.
done.
(gdb) run test.py
Starting program: /usr/bin/python3 test.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffeb420700 (LWP 29546)]
[New Thread 0x7fffded9b700 (LWP 29547)]

Program received signal SIGSEGV, Segmentation fault.
threadstate_getframe.lto_priv.1748 (self=0x0) at ../Python/pystate.c:167
167     ../Python/pystate.c: No such file or directory.
(gdb) bt
#0  threadstate_getframe.lto_priv.1748 (self=0x0) at ../Python/pystate.c:167
#1  0x00000000004ffdf8 in PyEval_GetFrame () at ../Python/ceval.c:4052
#2  PyEval_GetGlobals () at ../Python/ceval.c:4040
#3  PyImport_Import () at ../Python/import.c:1814
#4  0x00000000005000ff in PyImport_ImportModule () at ../Python/import.c:1313
#5  0x00007fffdd7308d3 in QPythonPriv::QPythonPriv() () from /usr/lib/x86_64-linux-gnu/qt5/qml/io/thp/pyotherside/libpyothersideplugin.so
#6  0x00007fffdd726f75 in QPython::QPython(QObject*, int, int) () from /usr/lib/x86_64-linux-gnu/qt5/qml/io/thp/pyotherside/libpyothersideplugin.so
#7  0x00007fffdd7211aa in void QQmlPrivate::createInto<QPython14>(void*) () from /usr/lib/x86_64-linux-gnu/qt5/qml/io/thp/pyotherside/libpyothersideplugin.so
#8  0x00007fffeef7efdb in QQmlType::create() const () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#9  0x00007fffeefe06c4 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#10 0x00007fffeefe2ac3 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#11 0x00007fffeefe2d58 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#12 0x00007fffeefdf2fa in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#13 0x00007fffeefdfc5a in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#14 0x00007fffeefe115f in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#15 0x00007fffeef69365 in QQmlComponentPrivate::beginCreate(QQmlContextData*) () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#16 0x00007fffeef66fcf in QQmlComponent::create(QQmlContext*) () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#17 0x00007fffeefd43b2 in QQmlApplicationEnginePrivate::_q_finishLoad(QObject*) () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#18 0x00007fffeefd45e2 in QQmlApplicationEnginePrivate::startLoad(QUrl const&, QByteArray const&, bool) () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#19 0x00007fffeefd47c0 in QQmlApplicationEngine::load(QString const&) () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#20 0x00007fffef17ae2e in ?? () from /usr/lib/python3/dist-packages/PyQt5/QtQml.cpython-34m-x86_64-linux-gnu.so
#21 0x0000000000511a1a in call_function (oparg=<optimized out>, pp_stack=0x7fffffffd7e0) at ../Python/ceval.c:4237
#22 PyEval_EvalFrameEx () at ../Python/ceval.c:2838
#23 0x0000000000515c2f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7fffffffd8e0, func=<optimized out>) at ../Python/ceval.c:4334
#24 call_function (oparg=<optimized out>, pp_stack=0x7fffffffd8e0) at ../Python/ceval.c:4262
#25 PyEval_EvalFrameEx () at ../Python/ceval.c:2838
#26 0x0000000000519757 in PyEval_EvalCodeEx () at ../Python/ceval.c:3588
#27 0x00000000005ee4cb in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=<optimized out>) at ../Python/ceval.c:775
#28 0x00000000005f7452 in run_mod () at ../Python/pythonrun.c:2175
#29 0x00000000005f958a in PyRun_FileExFlags () at ../Python/pythonrun.c:2128
#30 0x00000000005f9d3d in PyRun_SimpleFileExFlags () at ../Python/pythonrun.c:1601
#31 0x0000000000619235 in run_file (p_cf=0x7fffffffdb40, filename=<optimized out>, fp=0xaca070) at ../Modules/main.c:319
#32 Py_Main () at ../Modules/main.c:751
#33 0x00000000004c27df in main () at ../Modules/python.c:69
#34 0x00007ffff7811a40 in __libc_start_main (main=0x4c2700 <main>, argc=2, argv=0x7fffffffdd58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd48) at libc-start.c:289
#35 0x00000000005bdf09 in _start ()
(gdb)

If I just remove the Python component, the crash disappears.

Sorry for the lack of symbols in pyotherside (no pyotherside-dbg package on Ubuntu), but I hope the crash should be reproducible from this description.

Documentation request: PyOtherSide on Android

A low-priority documentation request.

I'm intrigued at the possibility for PyOtherSide on Android, as was hinted at in the Qt Developer Days presentation. I understand this would involve bundling Qt, Python, and PyOtherSide with my app, but I have no experience with Android and don't know where to begin. A tutorial or guide on how this might be possible would be very welcome.

Crash with Qt 5.4.2 when .py files are loaded from Qt Resources

I'm attempting to run the listmodel example on OS X Yosemite to no avail. Below is the head of the problem report and the stack trace of the crashed thread. It looks like something is going awry when converting the python list to a javascript array? Out of curiosity, I modified the example a bit so that the python function simply returned a string and everything worked just fine.

I've installed a packaged version of Qt 5.4.2, am using python 3.4.3 installed via homebrew, and pyotherside 1.4.0 has been installed as described in the project README.

I've searched for a solution, but didn't have much luck. Any help/suggestions are greatly appreciated! Also, let me know if you need additional details to help diagnose the problem.

Head of the problem report

Process:               listmodel [6794]
Path:                  /Users/USER/Desktop/*/listmodel.app/Contents/MacOS/listmodel
Identifier:            com.yourcompany.listmodel
Version:               ???
Code Type:             X86-64 (Native)
Parent Process:        Qt Creator [6542]
Responsible:           Qt Creator [6542]
User ID:               501

Date/Time:             2015-06-02 19:56:23.064 -0400
OS Version:            Mac OS X 10.10.3 (14D136)
Report Version:        11
Anonymous UUID:        CA7E2107-3D4F-A7A7-372B-481ED0A7F257

Sleep/Wake UUID:       179C37EC-B84A-4BEE-A3EC-E9535903CE1D

Time Awake Since Boot: 6000 seconds
Time Since Wake:       2000 seconds

Crashed Thread:        8  QPythonWorker

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000008

Stack trace of the crashed thread

Thread 8 Crashed:: QPythonWorker
0   libpyothersideplugin.dylib      0x00000001161e4a9f PyObjectConverter::type(_object* const&) + 255
1   libpyothersideplugin.dylib      0x00000001161e5972 QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) + 146
2   libpyothersideplugin.dylib      0x00000001161e9b42 QPythonPriv::call(_object*, QString, QVariant, QVariant*) + 306
3   libpyothersideplugin.dylib      0x00000001161df80b QPython::call_sync(QVariant, QVariant) + 971
4   libpyothersideplugin.dylib      0x00000001161e62c5 QPythonWorker::process(QVariant, QVariant, QJSValue*) + 69
5   libpyothersideplugin.dylib      0x00000001161ec4b6 QPythonWorker::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 390
6   org.qt-project.QtCore           0x000000010e8c73b3 QObject::event(QEvent*) + 755
7   org.qt-project.QtWidgets        0x000000010f28eddb QApplicationPrivate::notify_helper(QObject*, QEvent*) + 251
8   org.qt-project.QtWidgets        0x000000010f2920f8 QApplication::notify(QObject*, QEvent*) + 8136
9   org.qt-project.QtCore           0x000000010e89ca7b QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) + 971
10  org.qt-project.QtCore           0x000000010e8eedbb QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 59
11  org.qt-project.QtCore           0x000000010e89939d QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 381
12  org.qt-project.QtCore           0x000000010e68ea95 QThread::exec() + 117
13  org.qt-project.QtCore           0x000000010e692523 QThreadPrivate::start(void*) + 339
14  libsystem_pthread.dylib         0x00007fff8a639268 _pthread_body + 131
15  libsystem_pthread.dylib         0x00007fff8a6391e5 _pthread_start + 176
16  libsystem_pthread.dylib         0x00007fff8a63741d thread_start + 13

Support conversion of JS Boolean variables to Python bool

I have a problem where a JavaScript variable with a boolean value is not being passed to the Python function correctly, and I end up with an empty Python dict.

qmlexperiment.py contains:

def pyfoo(bar):
    print('python - bar is: {0}'.format(bar))

qml contains:

import QtQuick 2.0
import io.thp.pyotherside 1.2

Python {

    onError: {
        console.log('python error: ' + traceback);
    }

    function foo() {
      importModule('qmlexperiment', function() {
            var bar = new Boolean(true);
            print('qml - bar is: ' + bar);
            call('qmlexperiment.pyfoo', [bar], function(result) {});
        });
    }

}

output:

qml - bar is: true
python - bar is: {}

Am I doing something wrong, or should the function be being passed a Python boolean?

If bar is replaced with true in the call args, then the Python function is correctly passed in 'True':

qml - bar is: true
python - bar is: True

Strange behavior with stdout/stderr from Python

Sorry for the slightly vague issue title, but I'm hoping to get some clarification.

With the test case

main.qml:

import QtQuick 2.0
import io.thp.pyotherside 1.4

Rectangle {
    color: 'black'
    width: 400
    height: 400

    Python {
        id: py

        Component.onCompleted: {
            addImportPath(Qt.resolvedUrl('.'));

            importModule('main', function () {
                py.call('main.testprint', [], function(result) {
                    console.log('printed from QML');
                });
            });
        }
    }
}

main.py:

def testprint():
    print('printed from python')

I get this behavior:

estan@newton:~/printtest$ qmlscene main.qml
printed from python
qml: printed from QML

as expected. The console.log message from QML is printed on stderr (since results in a qDebug()). And the print from Python shows up as well, presumably on stdout (?).

However, if I do

estan@newton:~/printtest$ qmlscene main.qml > test.out
qml: printed from QML
estan@newton:~/printtest$ cat test.out

you can see that I seem unable to redirect the stdout from the Python process into a file. I guess this has something to do with how pyotherside starts the Python process?

I think related to this is the fact that running the test case from inside Qt Creator, I can't see the Python printouts, because the redirection that Qt Creator sets up can't "see" them.

Could the way pyotherside starts the Python process possibly be changed, so that the two processes share file descriptors? Or is there some other way to make this more intuitive? It's a little awkward to have to set up logging to a file in order to even see the Python output when working from Qt Creator.

Support for image loading from Python data with QQuickImageProvider

First I would like to say I really like pyotherside and I'm looking forward to using it for a Qt5 GUI for both modRana and Mieru (for Sailfish and beyond :) ).

I've looked through the code and so far there seems to be only one important thing missing for my use-cases and that is being able to load images to QML from Python data objects. The use-cases look like this:

Tile loading in modRana

  • modRana downloads tiles from the internet and stores them in a sqlite database using Python
  • the QML map widget sends tile loading requests (using the Image element source property for the tiles) and the Python code fetches the tiles from the database (or from the Internet) in the form of Python data object, which is then converted to QImage and sent to the QML context
  • the current Qt4 GUI is using the QDeclarativeImageProvider through PySide to do this

Page loading in Mieru

  • the Python code uses zipfile/tarfile/rarfile to fetch single images from comic book/manga archives as needed
  • when the user turns a page, a new source property for the Image comprising the page is set, Python gets a request, fetches the image to Python data object (string of file like object, not sure at the moment), converts it to QImage and sends it to the QML context
  • again, the current Qt4 GUI uses the QDeclarativeImageProvider to accomplish this

Currently, these two (and other similar) use-cases doesn't seem to be possible with pyotherside (if I disregard such horrible hacks as creating a temporary file for each image and using file path loading or running a local webserver to abuse image loading over network).

This is how I think the current situation could be improved:

  • the C++ code registers a single QQuickImageProvider for some keyword (lets say "python")
  • the Python code would get the id & requested size and will return a string or a filelike object
  • once an Image has its source property set to correspond to to the "python" image provider:
    • the provider calls the Python-side callback and passes it the id and requested size
    • the Python code does what needs to be done and returns the string/file-like object
    • the provider converts the string/file object to QImage and returns it to the QML context
      I hope I didn't forget any obvious "gotchas" that would prevent this from working. :)

How could it look like ?

Simple example - Python:

import pyotherside

def get_image_data(image_id, width, height):
    # this is just a simplified example how you can get
    # some image data in Python, but in a real
    # application the data might come from a blob
    # in a sqlite database, from a zipfile, 
    # a rendering library, etc.
    f = open("images/%s.png" % image_id , "rb")
    data = f.read()
    f.close()
    return data

pyotherside.register_image_data_callback(get_image_data)

Simple example - QML side:

import QtQuick 2.0
import io.thp.pyotherside 1.0

Image {
    id : imageFromPython
    width: 200
    height: 200
    source : ""

    Python {
        // make sure the Python code
       // gets a chance to register the callback
        id: python
        Component.onCompleted: {
            addImportPath('examples/atexit');
            importModule_sync('main');
            // callback should be now registered
            // and source can be set
            imageFromPython.source = "image://python/foo"
        }

        onError: console.log('error in python: ' + traceback);
    }
}

Sure, advanced stuff like registering multiple providers with custom keywords could be also supported, but one hardcoded provider & one callback is IMHO all that would be needed for now.

So, what do you think think about this enhancement proposal ? :)

Cannot convert QVariant to QJSValue in example code

Using Qt 5.4.0 and Python 3.4.2 on Archlinux I can't get values from python on QML, due casting error. The evaluate one line example runs without problems.
The example (and also, there is a missing bracket on it) and the output:

Python {
    Component.onCompleted: {
        importModule('os', function() {
            call('os.getcwd', [], function (result) {
                console.log('Working directory: ' + result);
                call('os.chdir', ['/'], function (result) {
                    console.log('Working directory changed.');
                });});
            });
        });
    }
}

Cannot convert: QVariant(QJSValue, )
qml: Working directory: undefined
Cannot convert: QVariant(QJSValue, )
qml: Working directory changed.

Is it possible to cancel execution of some function?

In some cases (plugins, long running jobs, etc) qml/c++ code should be able to cancel execution of python code. Is it possible to implement this feature in pyotherside?

If it possible, please point me to the right direction and I will implement it by myself. Thanks!

Crash preceded by weird error messages

I have been reworking how map tiles are displayed in modRana and managed to crash PyOtherSide in the process:

https://gist.github.com/M4rtinK/b634432063f81beb034ae3c37d1a6e7b

It basically starts spamming error messages about properties that are definitely in scope not being available and then crashes. My hunch is that some sort of race condition is going on, possibly triggered by quite large bursts of QML <-> Python traffic the new tile display method causes.

Steps to reproduce
git clone https://github.com/M4rtinK/modrana
cd modrana
git checkout master-pyotherside_crash
cd run
./qt5.sh

If the crash does not happen at once try panning the map or closing modRana and opening it again - that should trigger the crash.

Additional information
My machine runs Fedora 23 and the same crash happens on both the latest stable (1.4) and on latest git master.

Let me know if you need any more info. :)

PyOtherSide crashes with Qt 5.4 & Python 3 on Fedora 21 when starting modRana

The crash manifests as a qmlscene segfault ( Segmentation fault (core dumped) ) during modRana startup. It appears to be caused by some sort of a race condition as it does not appear consistently (sometime modRana does survive the startup) and seems to be influenced by the speed of the computer it is running on. On fast modern computers it crashes in about 80% cases, on older PC-class hardware the crashes happen much less often (about 1-2 crashes in 10 starts). So even though I have not yet seen this happen on Sailfish OS, it might very well just be prevented by the longer time modRana takes to start on the Jolla.

I've reproduced the issues on different Fedora 21 versions to exclude the chance that some stuff specific to my computer is influencing the crashes. This includes a fresh baremetal Fedora 21 installation.

The following steps can be used to reproduce the issue:

sudo dnf install python3-devel qt5-qtdeclarative qt5-qtcontrols qt5-qtsensors git  (if starting on a fresh F21 install) 
git clone https://github.com/thp/pyotherside
git clone https://github.com/M4rtinK/modrana
cd pyotherside
git checkout 5b76536332f6b442f6e3d6310c72e7ab2c63451c
qmake-qt5
make
make install
cd ../modrana/run
git checkout 2021c12d2973ad9321e3d8f45727dfd00256f005
./qt5.sh

It might be needed to start modRana a few times to trigger the crash, especially on slower hardware. :)

Additional information

This shows up in the log:

led 20 21:40:13 hostname kernel: qmlscene-qt5[4694]: segfault at 0 ip 0000003f26eb09e8 sp 00007ffff1c67470 error 4 in libQt5Qml.so.5.4.0[3f26c00000+408000]

And this is the core_backtrace as generated by abrt for one if these crashes:
http://paste.fedoraproject.org/172171/14217874

Please let me know if you need any more information to debug this. :)

UPDATE 10. 2. 2015
Added checkout of a specific commits for PyOtherSide and modRana to make sure that the reproducer is deterministic.

Old pythonlib.zip not updated on package reinstall on Android

If a package is reinstalled on Android, local data is kept, including the cache folder and the pythonlib.zip file. As a result PyOtherSide keeps using the old zip file, even if it might have a newer one compiled in. This can cause issues as the old file might be missing some Python modules or worse - contains shared libraries compiled against a different Python version.

Reinstall happens when a user installs a package update (a package with higher Android version number) and can also be triggered manually:

adb install -r

A possible solution (until issue #30 hopefully makes extracting the zip file obsolete) could be to compile some sort build/pythonlib identificator (hash of the zip file, current epoch, etc.) into the PyOtherSide binary and use it as part of the filename when extracting the pythonlib.zip file: cache/pythonlib_.zip

On each startup PyOtherSide would check if cache/pythonlib_.zip exists, if it does not, it would first delete all old pythonlib files (cache/pythonlib_*.zip) and then would extract the zip from qrc and store it as cache/pythonlib_.zip.

"Advanced" python module imports

Low priority, but a 'nice to have' feature request:

  1. Selecting specific module for import: from x.y.z import a, b, c

  2. Aliasing a module to an alternative name: from x.y.z import a as b

Example: An application might have some specific code for use with pyotherside. It may make sense to call the module containing this code "pyotherside", however, this then might clash with the existing "pyotherside" module. Therefore when importing, it would have to be included with a different name.

from myapp.ui import pyotherside as ui

Segfault when trying to call a function.

I made a simple app for SailfishOS using pyotherside and it worked, but then I tried to run (after writing new qml) it on desktop (Linux) it crashed. So I wrote test code that also crashed.
My QML code:

import QtQuick 2.3
import QtQuick.Window 2.2
import io.thp.pyotherside 1.4
import QtQuick.Controls 1.4

Window{
    visible: true

    Button{
        id: button
        text: "Test"
        onClicked:{
            python.test();
        }
    }

    Python{
            id: python
            onError: console.log('Error: ' + traceback)
            onReceived: console.log('Event: ' + data)
            Component.onCompleted:{
                addImportPath(Qt.resolvedUrl("."));
                importModule("mytest", function(){});
            }

            function test(){
                call("mytest.testFunc", function(){})
            }
    }
}

Python code:

import pyotherside

def testFunc():
    pyotherside.send("testevent", "test")

When I press the button application crashes. Tested on Ubuntu 16.04 and Debian.
Here is full sources for Qt Creator https://www.dropbox.com/s/3kq5wolaqtxxb4n/TestApp.tar.xz?dl=0
Python 3.5.1+
Qt 5.5.1

Terminate running python call from QML

Maybe I'm missing something obvious.
The use case:
User starts a synchronization that will call a a python function that will download/upload files. This takes a while and the user might want to cancel the process. I'm looking for a way to send a signal from QML to the python interpreter to stop. Something like ctrl+c in the terminal.

importModule runs forever

import QtQuick 2.0
import io.thp.pyotherside 1.2

Rectangle {
    width: 300
    height: 300
    Python {
        Component.onCompleted: {
//            importModule('gi.repository.Gio', function(){
//                console.log('import completed')
//            })
            importModule_sync('gi.repository.Gio');
            call("gi.repository.Gio.Settings.new('org.gnome.Vino').keys", [], function(result){console.log(result)});
        }
        onError: console.log('Error: ' + traceback)
    }
}

Possibly an edge case with this particular library:
Synchronous call works just fine.
However its (commented out) counterpart runs forever and never reach the callback function. Closing qmlscene removes the visible window but the process is still live until manually killed.

No error handling in QML callbacks

When I send an event from Python to QML using pyotherside.send I'm not getting proper error handling on the QML side. If I have an error in my QML handler, it seems that execution silently stops at the offending point with no console output or crash.

I'm using pyotherside-qml-plugin-python3-qt5 1.0.0-1.17.1 on the Sailfish SDK. I use qmlscene to run my code and it handles errors properly except for those pyotherside.send handlers. Also, console.log works in those handlers, so I can manually find the offending line.

Support for date and time types

Allow passing of the following values between Python and QML:

Python C API: http://docs.python.org/3/c-api/datetime.html

Importing specific functions/objects from a module

The goal is to add possibility to do importNames('module', ['foo', 'bar']); in QML,
which corresponds to Python's from module import foo, bar.
After the call is successful one should be able to call foo, bar directly in the call:
call('foo', [], callBack);.

The feature corresponds to the pattern of having a "main" module of the application,
which the GUI connects to and exposes as an interface to a user.
Without the feature the calls to Python -- call( 'main.foo', [], callBack ); -- have unnecessary 'main...' repeated everywhere.

The idea comes from discussion of issue 15.

Cannot run two functions concurrently.

Do you have any plans on implementing multiprocesses or threading into pyotherside, without managing it myself? I do not mind doing it myself, I just think that it would clean up the code if you were able to set a flag in the Python {} component; or possibly adding something like:

Python {
//...
importModule('scan_system', function() {})
importModule('check_system', function() {
multiprocessCall('check_system.check', [], function(result) {console.log(result)},
'scan_system.scan, [], function(result) {console.log(result)} )
})
}

PyGLArea works with Window, but not ApplicationWindow

I've taken the pyglarea.qml demo and replaced the outer-most Item with Window from QtQuick.Window, and this appears to work as expected. If I instead use an ApplicationWindow from QtQuick.Controls, the PyGLArea does not show up. The other widgets show up, but on top of the grey background of the ApplicationWindow.

Is this a known limitation of PyGLArea? Is it possible to add support for using PyGLArea with an ApplicationWindow, or is there some restriction (e.g., from ApplicationWindow) that makes this difficult?

If this is a known limitation, it would be nice to have the docs mention the limitation.

compile error ( ‘QPython’ does not have any field named ‘gnu_dev_major’ )

Hi Thomas
There are some compiling errors in the latest version, which did not exists in the previous version. When changing back to previous version (1.0.0-8) ,everything is fine! Following is the error log:

[22:20] gordianknot@localhost source $ sudo make --always-make
.
.
.
.
In file included from /usr/include/sys/types.h:222:0,
from /usr/include/stdlib.h:314,
from /usr/include/python3.3m/Python.h:34,
from pyobject_converter.h:24,
from qml_python_bridge.h:22,
from qpython.cpp:19:
qpython.cpp: In constructor ‘QPython::QPython(QObject_, int, int)’:
qpython.cpp:38:7: error: class ‘QPython’ does not have any field named ‘gnu_dev_major’
, major(major)
^
qpython.cpp:39:7: error: class ‘QPython’ does not have any field named ‘gnu_dev_minor’
, minor(minor)
^
make[1]: *_* [qpython.o] Error 1
make[1]: Leaving directory `/home/gordianknot/Documents/pyotherside/source/src'
make: *** [sub-src-make_first] Error 2

Efficient loading ListModel data from Python

In this tutorial video, at 39:40, Gabriel de Dietrich emphasized on inefficiency of the following pattern:

for (var i=0; i<result.length; i++) {
    listModel.append(result[i]);
}

In his case, he solved that by sharing the same data from the C++ code.
But C++ is all Greek to me - hence my enthusiasm about pyotherside 😃

In my case, I have a 10k+ list of annotated coordinates (a PCB with a lot of holes). User is supposed to do a lot of hole rearranging and the GUI responsiveness is essential to the workflow.
Presumably without diving in to the C++ code and staying on the py side, how changes to the ListModel can be more efficient?

Cannot build on Windows

I am attempting to build with Python 3.3, and Qt 5.5.1. I have followed the official instructions up to installing the project.

Sorry, I am not familiar with the Python C api, but it seems something is wrong here. It seems to expect
PyObject* PyObject_CallMethod(PyObject *o, char *method, char *format, ...) as the signiture, but you seem to be calling with char arrays...

Got it building on Python 3.4 and Qt 5.5.1, however, now I am getting:

QML module does not contain information about components contained in plugins.

Module path: C:/Qt/Qt5.5.1/5.5/msvc2010/qml/io/thp/pyotherside
See "Using QML Modules with Plugins" in the documentation.

Automatic type dump of QML module failed.
Errors:
"C:\Qt\Qt5.5.1\5.5\msvc2010\bin\qmlplugindump.exe" returned exit code 3.
Arguments: -nonrelocatable io.thp.pyotherside 1.4 C:/Qt/Qt5.5.1/5.5/msvc2010/qml
QMutex: destroying locked mutex

whenever I try to use it. I am going to look to see if there are any easy fixes to this.

Add support for progress callback during calls to Python

When working with several concurrent calls to long-running Python functions it would be very convenient if the call API was something like:

call(var func, args=[], function callback(result) {}, function on_progress(progress) {})

Where the on_progress is a callable that, when provided, would would be made available as an on_progress kwarg on the Python side.

So for example, with a mymodule.py:

def do_stuff(arg1, arg2, on_progress=None):
    for i in range(3):
        sleep(1)        # Heavy work
        if on_progress is not None:
            on_progress(i)  # Report progress
    return 'Done!'

and the call from QML:

py.call('mymodule.dostuff', ['arg1', 'arg2'],
    function (result) {
        console.log('Result: ' + result);
    },
    function (progress) {
        console.log('Progress: ' + progress);
    }
);

which should output:

Progress: 0
Progress: 1
Progress: 2
Result: Done!

I know this functionality can be built on top of the existing send mechanism, but having it built in like this would save me from a lot of bookkeeping on the QML side to associate sent progress with the corresponding outstanding call.

Thanks a lot for pyotherside BTW. It looks very promising, and I'm fairly certain that we'll use it for the UI on our product, which is a machine for mineral analysis to be used in the mining industry.

doc: compilation instructions and pyotherside python module

Hey,

just noticed while I was testing pyotherside that I couldn't compile while following the README instructions:

qmake
make

because I would get "Python.h: no such file or directory" (even though I have python-dev installed). I had to do:

qmake pyotherside.pro -r -spec linux-g++-64 CONFIG+=debug CONFIG+=declarative_debug CONFIG+=qml_debug
make

as set in the Qt Creator project settings.

Also, is the pyotherside Python module documented anywhere except in the source code? I don't see it in the Doxygen documentation.

Cheers!

How to run Pyotherside for Android

First I tryed to build pyotherside for Android using python3.2 of python27 for android project as building pyotherside for Blackberry. I couldn't build it. Then I tryed to deploy Android app using M4rtinK's pyotherside android qt creator project ( http://www.modrana.org/platforms/android/pyotherside/initial_attempts/ ).
Steps:
*export ANDROID_NDK_ROOT="/home/edip/Public/android-ndk-r9c"
*/home/edip/Qt5.2.0/5.2.0/android_armv7/bin/qmake
*make
*make install
It looks successfully built pyotherside to Qt5.2.0/5.2.0/android_armv7/qml.
But when tested an example, blank screen appears. (https://github.com/thp/pyotherside/blob/master/examples/helloworld.qml ). By reason of the fact that Python files is not copied to the apk. I checked device Qt file location, there was ..lib/libthp.so and ..qml/io/thp/pyotherside/(libpyothersideplugin.so, qmldir) .
Qt Creator's warning output:

W/Qt ( 9187): assets:/qml/thp/main.qml:2 ((null)): assets:/qml/thp/main.qml:2:1: plugin cannot be loaded for module "io.thp.pyotherside": Cannot load library /data/data/org.qtproject.example.thp/qml/io/thp/pyotherside/libpyothersideplugin.so: (Cannot load library: link_image[1936]: 903 could not load needed library 'libpython3.2.so' for 'libpyothersideplugin.so' (load_library[1091]: Library 'libpython3.2.so' not found))

After copy paste Android and libs forlder in pyotherside android qt creator project to project folder, it builds without warnings.

Maybe It requires to edit java files for copying Python files to the apk and creating build scripts as Buildozer. Is it possible to install Pyotherside for Android?
Thanks

numpy.polyfit hangs weirdly

I'm not quite sure whether im right here, maybe this is numpy's issue (or my build of it).
when I run numpy's "polyfit" function on my jolla in a pyotherside app, it always hangs exactly at the same place without returning or throwing any error.
It works fine when running the same thing in the Interpreter on the phone, or in pyotherside on the desktop.
To investigate further, running the debugger on polyfit(a,b,1,w=c) showed me the last few lines before freezing:

(Pdb) 
--Return--
> /usr/lib/python3.4/site-packages/numpy/core/_methods.py(38)_any()->False
-> return umr_any(a, axis, dtype, out, keepdims)

(Pdb) 
--Return--
> /usr/lib/python3.4/site-packages/numpy/core/fromnumeric.py(1848)any()->False
-> return arr.any(axis=axis, out=out, keepdims=keepdims)
(Pdb) 
> /usr/lib/python3.4/site-packages/numpy/core/machar.py(233)_do_init()
-> i = i + 1
(Pdb) 
> /usr/lib/python3.4/site-packages/numpy/core/machar.py(234)_do_init()
-> k = k + k
(Pdb) 
> /usr/lib/python3.4/site-packages/numpy/core/machar.py(223)_do_init()
-> for _ in range(max_iterN):
(Pdb) 
> /usr/lib/python3.4/site-packages/numpy/core/machar.py(224)_do_init()
-> y = z
(Pdb) 
> /usr/lib/python3.4/site-packages/numpy/core/machar.py(225)_do_init()
-> z = y*y
(Pdb) 

Has anyone an idea what could be causing this?
I havent seen any bug like this yet.
To reproduce, I created a quick demo:
numpy bug showcase
run the app with sailfish-numpy in terminal on the jolla and press the button, it sould bring up the python debugger.

the version of numpy i use is build by installing dependencies and running "pip wheel numpy" on the jolla phone, then extracted into the app dir.
Any other numpy function I tried seemed to work flawlessly.
Thanks for any kind of help

More flexibility when importing python modules

Hi, a feature request here to allow more flexibility when importing/accessing python modules.

It would be great if importModule could accept an 'as' argument for the equivalent
python:

from myapp.ui import foo as bar

I also noticed that if importing a 'nested/namespaced' python module, e.g. myapp.ui.foo, then I can't use functions from it. It doesn't show an import error, but the functions aren't available when using call.

For example myapp.api.foo.get_station_list won't work, but if I import the namespaced module with addImportPath('myapp/api') then foo.get_station_list does work.

Adding import path and importing declaratively

This request is for a feature which I'm not sure is possible, but here it goes.

Consider the following test case:

test.qml

import QtQuick 2.0
import io.thp.pyotherside 1.4

Rectangle {
    color: "red"
    width: 100
    height: 100

    OtherItem {
        color: "blue"
        width: 50
        height: 50
    }

    Python {
        id: py

        Component.onCompleted: {
            addImportPath(Qt.resolvedUrl('.'));

            importModule("test", function() {
                console.log("imported test");
            });
        }
    }
}

OtherItem.qml

import QtQuick 2.0

Rectangle {
    state: "one"

    states: [
        State {
            name: "one"
        }
    ]

    onStateChanged: {
        console.log("state changed, calling some_func");

        /*
         * The below won't work, unless I do the addImportPath and importModule
         * here again:
         *
         * py.addImportPath(Qt.resolvedUrl('.'));
         *
         * py.importModule("test", function() {
         *     py.call("test.some_func", function () {
         *        console.log("some_func returned");
         *     });
         * });
         */
        py.call("test.some_func", function () {
            console.log("some_func returned");
        });
    }
}

test.py:

def some_func():
    print("hey")

which gives the following when calling qmlscene test.qml:

qml: state changed, calling some_func
"PyOtherSide error: Traceback (most recent call last):

  File "<string>", line 1, in <module>

NameError: name 'test' is not defined
"
Unhandled PyOtherSide error: Function not found: 'test.some_func' (Traceback (most recent call last):

  File "<string>", line 1, in <module>

NameError: name 'test' is not defined
)
qml: imported test

I guess I need the addImportPath / importModule in the onStateChanged since that handler is called before the Component.onCompleted on the Python component is called, due to the order in which the components are loaded. If I'd do the same in say a key press handler, it would be no problem, since the Python component would be fully loaded by the time the key is pressed.

It would be very convenient if I could do something like

Python {
    id: py
    importPaths:  [Qt.resolvedUrl('.')]
    imports: ['test']
    ...
}

and from then on be able to rely on that the 'test' module has been imported, no matter where in the visual tree I make use of the interpreter (py).

I'm not sure this is possible though?

Can not compile

Im under virtualenv and I got this error
Python.h: No such file or directory

I tried installing sudo apt-get install python-dev and sudo apt-get install python3-dev but still no luck.

I sort of managed to get away with this problem manually copying all the header files into the src directory but now it says undefined reference to PyGILState_Ensure` and heaps of undefined reference errors.

Still can not compile.

Spawning process with another Qt app using multiprocessing from Python side

I'm probably missing something obvious, but with the test case

test.py

from multiprocessing import Process

def run_app():
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtWidgets import QMainWindow

    app = QApplication([__file__])

    window = QMainWindow()
    window.show()

    app.exec_()

process = Process(target=run_app)
process.start()

test.qml

import QtQuick 2.4

import io.thp.pyotherside 1.4

Python {
    Component.onCompleted: {
        addImportPath(Qt.resolvedUrl('.'));
        importModule('test', function () {
            console.log('test.py imported');
        });
    }
}

running qmlscene test.qml gives

estan@newton:~$ qmlscene test.qml 
qml: test.py imported
WARNING: QApplication was not created in the main() thread.

and nothing shows up.

Running python3 test.py works fine of course.

Any idea why QApplication is giving a warning about not being created in the main thread? Both QApplications involved here (the one I spawn, and the one that drives the QML application) are created in completely different processes, so they should both be in the main thread of their respective processes, right?

(Some background, since I know this looks like a very contrived example: I'm working on the HMI GUI for an analysis machine. I communicate with the machine backend scripts using ZeroMQ sockets. When not running on the machine itself, I have a little simulator tool I built using Qt to simulate ZeroMQ messages from the machine. So for convenience while developing, I thought I'd just fire up the simulator process using multiprocessing from the Python side of my QML/pyotherside frontend, so that I get them both launched side by side.)

call_sync fails when used with parameters

I reduced this to the following test case:

import QtQuick 2.0
import io.thp.pyotherside 1.4

Rectangle {
    Python {
        Component.onCompleted: {
            addImportPath(Qt.resolvedUrl('.'));
            importModule('os', function () {
            call_sync('os.chdir', ['/']);
            });
        }
    }
}

This produces the error Unhandled PyOtherSide error: Not a parameter list in call to os.chdir:.

If I use call instead of call_sync, then it appears to work (including the callback if I'm calling a function that returns something useful. I believe call also works correctly when used without any parameters.

I'm using version 1.4.0, on Ubuntu Vivid (the distro packaged version 1.4.0-1).

Could you please evaluate the difficulty of porting pyotherside to Python 2.7?

pyotherside is a great project that makes Python support QML for the latest Qt lib.
However, It only support Python 3 which limited it's usage, especially there are still a large amount of python library support only Python 2. And pypy, the jit version of Python, is based on version 2.7 too.

Is there large gap could stop pyotherside be ported to Python2.7?
If it is possible, maybe we could try to do something on this.

pyotherside does not import in Ubuntu 14.04 in a python file

Apologies this is likely the wrong place to post this, but I do not see an IRC channel, forum, or mailing list listed for getting help with pyotherside. If there is one of the above please direct me there.

I am trying to work with pyotherside on Ubuntu 14.04 (version 1.2). When I add:

import io.thp.pyotherside 1.2

To my qml file it imports successfully.

However when I add:

import pyotherside

To a python file, it fails to import. Do I need to install something other than the pyotherside package on Ubuntu to access pyotherside from a python file?

running app with "import http.client" import fails half of the time

Hello thp,

I've found this while working on my sailfish app which does some REST API calls in the background.
Having pyotherside-qml-plugin-python3-qt5 and python3-base

Steps to produce the issue:

  1. Create a Sailfishos project on SDK with pyotherside 1.0 and a separate .py file
  2. Do the necessary in QML addImportPath, importModule, async call to call a simple function from the .py
  3. Add a simple function to the .py returning some value and include "import http.client" in the .py file (function does not have to be http.client related)
  4. Set up pyotherside 1.0 on the Jolla device
  5. Deploy to Jolla as RPM package

Result:
Approximately 50% the app will exit immediately after launch. Very very very annoying :) Once it's running it's stable. Need to re-re-run the app until it's good.
I attach a simple project where the issue is reproducible.
https://www.dropbox.com/sh/ekl38688f0jvzil/Qt15nhHEiJ

Statically linking Python and shipping with application

I did not see any way to contact you on either this github page or on your developer guide, since don't want to flood your issues section with questions. Since you are here anways, is there a way to build pyotherside statically with python, for easy distribution? If not, how do you go about distributing python with an application, besides just providing the user with the standard python msi installer? Thanks.

Calling python functions from dynamically created objects

I have QML files File1.qml, File2.qml and PythonStuff.qml.

In PythonStuff.qml I declare

Python{
    id: python
    //functions, handlers, etc
}

I can call functions in FileN.qml by adding

PythonStuff{
   id: pythonStuff
}

to them and everything works perfectly.

But I need to create objects dynamically and call functions from PythonStuff.qml from them.
I create objects using CreateObject.js

var createdPages = []; 

function createPageObjects(jid){
    var component;
    var page;
    component = Qt.createComponent("DynPage.qml");
    page = component.createObject("dynPage", {"header": header});

    if(page == null){
        console.log("Error creating DynPage object");
    }
    return page
}�

I also have

PythonStuff{
   id: pythonStuff
}

in DynPage.qml to be able to call functions from PythonStuff.qml
It works, but when I call any function from any of FileN.qml or dynamically created objects, functions in my python script executes (1+number of created objects) times!
If I have

function disconnect(){
    console.log("disconnect()")
    call("pythonsript.con.disconnect", [], function(){})
}�

in PythonStuff.qml
and

def disconnect(self):
    pyotherside.send("log", "disconnected")�

in pythonscript.py
I have "disconnected()" message printed once and "disconnected" printed (1+number of created objects) time.
Is it some kind of flaw in pyotherside or I just do it incorrectly?

[RFC] No support for model implementations in Python

In C++, I can write an AbstractItemModel implementation. I'd like to do the same thing in Python so that I can have Pythonic data structures that can be manipulated naturally by a Python backend and the data reflected naturally with a QML frontend.

I have a basic implementation of this working. It needs considerable cleaning up and isn't ready yet, but I thought it would be useful to get some initial review and feedback on the basic design and API at this stage. I've pushed it to https://github.com/basak/pyotherside/tree/models. Some rudimentary documentation is at https://github.com/basak/pyotherside/blob/models/docs/models including an example, along with a basic model implementation in Python in https://github.com/basak/pyotherside/blob/models/docs/pymodel.py. Here are some notes copied from my initial documentation for your reference:

  • This is still a work in progress and the API between the QML PythonItemModel
    object and Python will change, so please do not merge this yet.
  • I expect to rebase this branch before submission.
  • There end up being three "faces" in Python. Suggestions for better names
    appreciated:
    1. The Python data object that reflects the data and manipulated from Python
      Pythonically. The availability of this object is the goal, and I'm
      currently calling this the "Python Side". This is how a Python backend
      speaks in Python about the model.
    2. The Python data object that is passed through to the QML PythonItemModel
      object that it can query for data as required. This is an API I've
      invented that quite closely matches the AbstractItemModel Qt API and I'm
      currently calling this the "Other Side". This is how QML speaks to Python
      about the model.
    3. The Python data object that is presented by the QML PythonItemModel and is
      used to call the PythonItemModel back, for example to inform it when
      changes. I'm currently calling this the "bridge". Again this is an API
      I've invented. This is how Python speaks to QML about the model.
    4. Symmetry suggests that there should be a fourth face. I suppose this is
      the PythonItemModel object in QML itself. This is how a QML frontend
      speaks in QML about the model.
  • I wonder if there will be a significant performance impact in putting the
    model into Python. I guess I'll need to try it and see.
  • pymodel currently provides ListModel and SortedListModel. Other things like
    hierarchical models and allowing users to write their own entirely in Python
    are possible with the current C++ implementation.
  • In Qt, AbstractItemModel's API isn't thread-safe in that the model must not
    be modified except from the UI thread, so that while a view is retrieving
    data the data remains consistent. This requirement is passed on to the Python
    model implementation. I have done this using the in_ui_thread decorator in my
    example (which we should ship).
  • Currently in the Python API provided by PythonItemModel there are two ways of
    representing a QModelIndex - using integer references and using lists of
    (row, column) tuples. The latter seems more consistent (since
    signal_dataChanged must use it) so the other method should be removed and the
    C++ side adapted.
  • signal_dataChanged should be called emit_dataChanged.
  • The C++ end is a complete mess and needs significant cleaning up.
  • Naming conventions are all over the place since python_uses_underscores and
    C++ uses camelCase. Consistency could be improved even if using both by
    deciding when it is appropriate to use each.
  • pymodel.py should be called something else and perhaps just be made available
    as part of the pyotherside module import. This needs build system thought
    since pyotherside currently doesn't ship any Python.
  • I want to keep the use of pymodel.py optional, and maintain the API between
    PythonItemModel and Python formally. Then pyotherside users will have maximum
    flexbility in maintaining their own custom models written in Python.
  • Feedback appreciated!

Ship Python Standard Library as .zip appended to plugin .so

Right now, on Android, we ship the Python Standard Library in the Qt Resource System and extract it on first start. Instead of this, it should be possible to append the .zip directly to the PyOtherSide plugin .so and then add the .so filename (obtained via dladdr(3) for example) to the PYTHONPATH.

How to use other module like bs4,PIL,etc..

I see image_loader.py in youer examples, and found you use this code:

from PIL import Image
from PIL import ImageDraw

I want to use bs4 and PIL module in my sailfish app,i don't know how to use them.
Please help me ,thanks

syntactic sugar for callback functions

⚠️ noob behind the keyboard ⚠️

👑 As this QML plugin had already received The 100% Pure Awesomeness Reward
it just need one more thing for The Universe Ultimate Awesomeness Reward:

How can one clean this "mess" of nested function callbacks?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.