Python support do i miss someting?

ahoj
i was looking into some cross platform languages, tried this or that and finally decided to go with python
i have implemented all my application logic plus persistance in python and it works nicely from cmdline
now i ve started to glue it with qml, but honestly, is that it ?

    function startDownload() {
        page.downloading = true;
        dlprogress.value = 0.0;
        call('datadownloader.downloader.download', function() {});
    }

i have now basically an appcontroller in python that i did wanted to instantiate once and assign it to applicationWindow: done
now when i see the example code, it seems i would need a wrapper for each method ?

yes, i could have checked earlier, but the first sentence:
In addition to C++, Python is a fully supported language for developing Sailfish OS applications. It …

yes i will now did into pyotherside docs but if someone knows a nice example of something more then calling some fu’s
would save me a lot of time
thanks!

1 Like

Not very knowledgeable about this, but I think the mathematical apps Integral, Derivative, and Limit by rcolistete might be instructive here:

1 Like

the problem is that they mainly execute a calculation, thus it is a method call with arguments.

but i have now an oo model

what i saw sofar in the documents and tried out:
you can have python intepreter multiple times. they share the context.
so i can create a global var and init it on start then access it per global variable.

while that would work, it means i would need to write a wrapper fu for each method …

You need callbacks. You can implement a generic callback method that takes variable arguements. Generally, one to one callbacks are quick and dirty. But building a qml/javascript generic object mapper is possible. I’d need to see your python to comment further.

1 Like

You might look at Multimodal as it has some interesting methods and has both intelligent (python) use of hardware and SQL, among other things. But I can’t remember how he wired it. Just that I have some todos. EDIT: https://github.com/poetaster/harbour-multimodal is an sdk friendly version.

1 Like

Actually, that’s not really true. I was being ‘square’. If you look at the cookbook, you will see a block on continuation style and the complementary callback style: PyOtherSide Developer Guide — PyOtherSide 1.5.9 documentation The snippet in question is ‘direct’:

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.');
                }););
            });
        });
    }
}

And, since I’ve now read the whole thing again, the way it’s done in Multimodal is also documented in the cookbook:

Python {
    signal updated()
    signal newEntries(int first, int last)
    signal entryRenamed(int index, string name)

    Component.onCompleted: {
        setHandler('updated', updated);
        setHandler('new-entries', newEntries);
        setHandler('entry-renamed', entryRenamed);
    }
}

which corresponds to the python side:

pyotherside.send('updated')
pyotherside.send('new-entries', 20, 30)
pyotherside.send('entry-renamed', 11, 'Hello World')

thank you for your help.

my code is here: https://github.com/PawelSpoon/olive-goes-shopping
python subfolder does contain the controllers and the test

idea was to have assetmanager to manage phydims, units etc and to use it in qml
now i did stumble over second python specific: i can not pass a reference.

so i can have a central assetmanager to instantiate and init all controllers, but i can not use assetmanager to store all at once, as the copies to contain the new data.
this is what i am changing now:

  • moving store into the controllers.

from what i saw sofar i can init all in importModule call
then i will need a wrapper for get and store at least
as i did implement all the checks in the controllers, i would need to write wrappers for those methods too

To make the advice about Multimodal more specific, take a look at

  • harbour-multimodal.qml
  • MainHandler.qml
  • PythonHandler.qml
  • MainPage.qml

To see how you can do signal/slot llke wiring:

In Main for instance, listmodels get wired to signals :

  Component.onCompleted: {
    app.signal_settings_loaded.connect(settings_loaded)
    app.signal_a_search_stop.connect(list_model.a_search_stop)
    app.signal_a_geo_stops.connect(list_model.a_geo_stops)
    app.signal_a_stops_by_ids.connect(list_model.a_stops_by_ids)
    app.signal_a_get_disruptions.connect(a_get_disruptions)
    app.signal_position_update.connect(list_model.position_update)
    app.signal_error.connect(error_handler)
  }

app is the id of the ApplicationWindow where initial signals are set up.

i guess it is a very nice structured and written app, but it only shows:
you have one object (in this case pythonhandler) that exposes a million of methods …

i mean i could have written one flat file with static functions in py and it would be easier to connect with qml then what i have done here.

what a waste of my time :slight_smile:

and even that:
how would i wrap something like that into :slight_smile:
assetMngr.getController(arg).getList()

call_sync('app.assMgr.getController(…arg…).getList()

without doing string operations ?
i see that i can pass args to a method, but not to a method on the way …

1 Like

Waste of time or learning experience, see it as you will. :slight_smile:

I haven’t been able to find a better solution yet other than invoking each method individually.

Maybe building some kind of master method in python and passing keyword arguments could work but I didn’t think it through so this may be a dead end as well.

I was hinting at something like this when I began. On the python side you have a function which passes variable signal names and params and likewise on the javascript side. Somthing like:

def sendDelegate(name, [args])
  pyotherside.send(name, arg, arg, etc)

And a generalization on the other side:

function setHandlerDelegate(name [args]) {
  setHandler(name, args);
}

I haven’t the time right now, but will give something like this a crack.

1 Like

i am in bed with covid, as soon as i am fit to work i will not have time left to finish it
so i did spent one day to reimplement all in python
now struggling the second day with pyotherside
no i do not consider it as learning something usefull…

the other fun fact:
if i return a list of strings, it arrives as json array
if i return a dict it will be object, so far to true
now if i return list(dict.values()) it should arrive as json array on qml side, only it does not
it arrives as vectormap and i need to convert it

I don’t understand. The Type mappings in both directions are flexible. A python dict is a JS object that can be used much like a dict. List, tuple and set are then JS arrays. If you’re call to getList() returns a dict you can do stuff like this (no callback):

    Python {
        id: py
        Component.onCompleted: {
            importModule('listmodel', function () {
                py.call('listmodel.get_data', [], function(result) {
                    for (var i=0; i<result.length; i++) {
                        listModel.append(result[i]);
                    }
                });
            });
        }
    }

and there are lots of variants.

I sometimes prefer the c++ signal and slots mechanisms, but this is, in my opinion easier to understand and use than all in c++?

Ah, damn. Get well soon! Stop programming and watch a movie!

1 Like

wasted already too much of my lifespan with movies …

Ah, your right. back to hacking. Get well!

i used this
evaluate(‘app.app.getAssetManager().getController("’+type+’").getAsList()’)

getList would return a dic (i know …)
getAsList returns list(dic.values())

i pass it on to standard fillmodel code yup

and i use evaluate cause call_sync returns: unhandled error received: Not a callable

and the reason will be the parameter in getController. i have to flatten it in the wrapper

def getAssetList(self, type):
    return self.assetManager.getController(type).getAsList()

then i can use call_sync

i can load the basics now.
but can not store back :slight_smile:
i did deploy all data files into /usr/share … - seems not writeable
can not find an rpm macro for that:

AppLocalDataLocation “~/.local/share/”, “/usr/local/share/”, “/usr/share/”

literally nowhere, so now i am reusing sailjail migration code, what a shame

You’re overcomplicating things! But I understand. I’m just lazy enough to simply dump all the python stuff in either a subdir of ‘/qml’ or in ‘/usr/share/myapp/lib’ …

In an older version of Imageworks, which included python library stuff I did this in the .pro (so qmake, not rpm):

equals(QT_ARCH, i386): {
  python.files = lib/x86_32/*
  DISTFILES += lib/PIL/x86_32/PIL/* \
  message("!!!architecture x86 / 32bit detected!!!");
}
equals(QT_ARCH, arm): {
  python.files = lib/arm32/*
  DISTFILES += lib/arm32/PIL/* \
  message("!!!architecture armv7hl detected!!!");
}

python.path = "/usr/share/harbour-simplecrop/lib"
INSTALLS += python

So, basically, take the files from project root, lib, x86_32 and install em to /usr/share/harbour-simplecrop/lib.

Are you developing with the SDK? @cypherpunks is just using rpm and working directly on device, so he does this kind of stuff differently.