BlackBerry-Tart Preview


Posted:   |  More posts about Python BB10 Tart BBPyProject

As I mentioned in a couple of tweets earlier today I've achieved some excellent initial success with a new approach. The name's just a silly pun... a "tart" is lighter-weight than a "pie", and BlackBerry-Tart is (much) lighter-weight than BlackBerry-Py.

The previous BlackBerry-Py stuff was focused on the PySide package and using the Qnx/Python entry point to launch the Python interpreter directly, using wrapped Qt libraries for the GUI portions (and others) of the app. This worked out quite well, especially on the PlayBook where we don't have Cascades yet. One downside was the size of the .bar files, at roughly 12MB minimum and maybe over 20MB with everything bundled.

For the Cascades version, Micke Prag began work on extending PySide to include a wrapper library for libqtcascades.so. That seemed to have worked pretty well, though I've had difficulty reproducing the results so far. Although everything seemed to build fine, it wouldn't run. Probably it just needs some tweaking of a few CMake files or command line arguments somewhere, but I confess to being rather stale in my C++ knowledge and certainly don't have the experience needed yet to troubleshoot that efficiently.

That roadblock led me to consider another approach. It's actually a bit of a return to the original BBX-Python launcher concept, but with Cascades in the picture it's a far, far different level that we're on.

I won't go into full details yet... need to pack for BlackBerry Jam Americas and also spend some more time bulking things out to see where the problem areas might be. For now, I'll just ramble for a moment to summarize the basic idea.

Rather than using the Qnx/Python entry point, this is technically a native app so it runs as Qnx/Elf instead. There's an app built on the basic Cascades template from Momentics, so it launches, sets up the bb::cascades::Application object, loads a QML document and sets it as the root/scene (my Cascades terminology is still fuzzy), and runs it.

Before it calls app::exec(), however, I have it create a controller object called Tart, which launches a TartThread and a TartQueue. The thread creates the Python interpreter, installs some callback functions as part of a new builtin module it calls "tart", and executes a .py file pointed to by the command line in much the same way the original launcher did.

The Python code can do what it wants to initialize things, and then after an import tart it is expected to run an event loop, much like this:

import tart

while True:
    msg = tart.wait()   # block on incoming messages from Qt/QML

    # do stuff with message

    # possible async responses emitted from Python, even from other threads
    tart.send(response='foo bar', data=3.14)

The above overlooks some details, obviously, with the main one right now being some JSON decoding. To get things working quickly at this early stage, I support just simple string messages, but by encoding them as JSON I get a highly flexible protocol with just the overhead of the encode/decode step in both directions. I haven't measured that overhead but, if that worries you, just know that it doesn't have to stay this way, and anyway we should avoid premature optimization.

The C++ code meanwhile goes off and calls app::exec() to launch the Cascades portion of the app, which displays the QML and does all those Cascadey things we want. There's a small set of machinery in between the QML and Python to pass the messages back and forth as required, for now treating them as opaque strings and not inspecting or interfering with them in any way. The beauty of this is it's connecting one dynamic language (JavaScript) with another (Python) and that nasty old static C++ doesn't need to be changed all the time. Bwahahahaha!

The QML code can connect to incoming messages from Python somewhat like this, because the Tart object has installed itself for access with qml->setContextProperty("tart", tart);:

function onMessage(msg) {
    console.log('incoming ' + msg);
}

onCreationCompleted: {
    tart.messageYielded.connect(onMessage);
}

At some point the user might do something so we can react to that:

Button {
    text: "Do Stuff"
    onClicked: {
        tart.postMessage("do stuff, please");
    }
}

That message goes down to the C++ Tart object, into the TartQueue in a threadsafe manner, and is then picked up by the main Python thread which has called back into the C++ using the tart.wait() call in the new builtin module. That immediately wakes up the main thread, delivers the new message to it, and allows it to process and respond as required.


For the very enterprising and capable among you, I'll give enough extra info to let you actually use this as-is. It shouldn't be hard, but you'd do well to have a good handle on the blackberry-xxxpackager command line tools, the structure and content of .bar files, and how to write both Python and JavaScript code. Knowing some QML wouldn't be a bad idea either...

The two pieces you need are the quickie demo I slapped together, and the command that was used to build it.

For the first, go to http://blackberry-py.microcode.ca/downloads/ and grab the latest BBTartTest-x.x.x.x.bar file, whatever the version is. You'll want to sideload it on your Dev Alpha (BB10 only) just to try it out. Then on your PC unzip it to get the tart-launch executable and inspect the other pieces.

For the second piece (DOS batch file syntax <shudder>):

@echo off
call blackberry-nativepackager -package BBTartTest.bar ^
    -devMode ^
    -target bar ^
    -env PYTHONDONTWRITEBYTECODE=1 ^
    -arg app/native/blackberry_tart.py ^
    bar-descriptor.xml ^
    tart-launch ^
    icon.png ^
    blackberry_tart.py ^
    assets/ ^
    -debugToken ..\debugtoken.bar

You should note that the path to the main Python file is specified as the final option on the command line (look in the MANIFEST.MF file in the .bar if you want to see that).

If you don't want my default .qml path of app/native/assets/main.qml to be used, you can override that by specifying -qml path/to/file.qml but this must be before the final argument which as noted is the main .py file. You'd do that by inserting -arg -qml -arg path/to/file.qml ^ in the above build script above the other -arg line. Watch that each line in the file has a trailing ^ if it's not the last one... believe it or not that's the DOS line-continuation character. Go figure.

If you read this, track me down at BlackBerry Jam Americas and tell me you did, and you'll get a 100% discount on the next release of BlackBerry Tart! Everyone loves to get something for free, right? ;-)

Comments powered by Disqus