Hey Brythonistas! Struggling to work out how to allow users to export data from my app.

44 views
Skip to first unread message

Andy DʼArcy Jewell

unread,
Jan 18, 2024, 7:30:12 PMJan 18
to brython
Hi, salut.

I'm new here, so I hope this question isn't asked in correctly or whatever.

I'm dabbling in writing a small app in Brython - I've messed round with it in the past, but have decided that it's probably a lot easier to write apps for use on mobile devices as web pages in Brython rather than as Android apps using Kivy. For my purposes, a web app seems to be fine, but I'd much rather not have the app reliant on a remote server for processing the simple calculations I have in mind, and Brython fits that bill perfectly - although I could, I really DON'T want to wrangle it in JS - I simply prefer Python syntax!

I have a working prototype of my app, and I'm loving how easy it was to get to this stage. The app can now save and load data to localstorage, and all the other main functions, so now I'm fleshing it out, adding features. One thing I need to add is the ability to export data to files and reimport it on another device, and this is where I'm getting stuck. 

For consistency with other interface elements, I'd like these to be triggered by clicking a button. I *think* in JS I could use URL.createObjectURL() to create a link that the user could click to download the data, but as I said, I'd like to make it a button rather than a link. 

Is this possible? If so, what am I missing?  I'm sure it's something easy really!

Thanks in advance,
Andy


Ed

unread,
Jan 18, 2024, 9:09:06 PMJan 18
to bry...@googlegroups.com
Good question.  I don't know android apps, but I did manage to export data from brython in chrome.  It was tricky, I found a lot of ways that _should_ work but didn't.

Hopefully this gets you started.  I left in the non-working approaches as comments.

# -----------------------------------------------------

log = console.log


def export_data (items) :

    'export data as a file'


    # ----- convert items to string


    result = window.JSON.stringify (items)

    log (f'result = { result }')


    # ----- format timestamp, you can skip this part


    date = window.Date.new ()

    datestr = date.toISOString ()  # '2021-10-12T20:15:17.948Z'


    stamp = datestr

    stamp = stamp.split ('.', 1) [0]  # drop ms

    stamp = stamp.replace (':', '_')

    stamp = stamp.replace ('T', '.')  # '2021-10-12.20_15_17'


    # ----- save data as file


    # **** chrome btoa wont handle non-ascii data.

    # need to find another btoa solution. js lib?


    # doesnt work, zero output

    #blob = window.Blob.new ( window.Uint8Array.new (result) )


    # doesnt work, zero output

    #blob = window.Blob.new (['\ufeff' + result] , { 'type' : 'text/csv;charset=utf-8'} )


    # doesnt work, zero output

    #blob = window.Blob.new ( window.Uint8Array.new (result) )


    # finally, this **** works!

    blob = window.Blob.new ( [result] )

    url = window.URL.createObjectURL (blob)

    # url is now a working link to download the data as a file


    # my code runs in a chrome extension. I use chrome.downloads to trigger the download without any dialogs

    # if android has similar functionality you can use that instead

    # or trigger the download with normal js (with normal save dialogs)

    # or insert the url in a new link / control and have the user launch it manually

    chrome.downloads.download ({

        'url' : url ,

        'filename' : f'downloads.{ stamp }.json'

    })


Hope this helps.
Ed



--
You received this message because you are subscribed to the Google Groups "brython" group.
To unsubscribe from this group and stop receiving emails from it, send an email to brython+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/brython/2e35cf8d-3b02-4abc-9226-3a2592c64f20n%40googlegroups.com.

Andy DʼArcy Jewell

unread,
Jan 19, 2024, 4:41:22 AMJan 19
to brython
Thanks for the swift reply, Ed. I'll give that a try. I think it fills in some details I was missing!

By the way, I'm kind of avoiding making Android apps by having it as a web page running Brython instead. I just use Firefox on my phone to load the page from my RasPi web server over the wifi. I had tried using Kivy, butthe build and deploy seems overly complex and time-consuming. I like that with Brython I can just save the file to the web server and it's immediately available on my phone.

Andy DʼArcy Jewell

unread,
Jan 19, 2024, 5:16:26 AMJan 19
to brython
Ok, first question - what is your window object's class, i.e. how do you instantiate it? I can't see a Window class, or anything like it mentioned in the docs. I'm just using the DOM, so is this method translatable?

On Friday 19 January 2024 at 02:09:06 UTC Edward Elliott wrote:

Kiko

unread,
Jan 19, 2024, 5:48:32 AMJan 19
to bry...@googlegroups.com
2024-01-19 11:16 GMT+01:00, Andy DʼArcy Jewell <andy.dar...@gmail.com>:
> Ok, first question - what is your window object's class, i.e. how do you
> instantiate it? I can't see a Window class, or anything like it mentioned
> in the docs. I'm just using the DOM, so is this method translatable?

window is the window document object.

See here: https://www.brython.info/tests/editor.html?lang=es&code=from%20browser%20import%20window%0Aprint%28dir%28window%29%29
>>> <https://chrisrng.svbtle.com/using-url-createobjecturl> to create a link
>>>
>>> that the user could click to download the data, but as I said, I'd like
>>> to
>>> make it a button rather than a link.
>>>
>>> Is this possible? If so, what am I missing? I'm sure it's something easy
>>>
>>> really!
>>>
>>> Thanks in advance,
>>> Andy
>>>
>>>
>>> --
>>> You received this message because you are subscribed to the Google Groups
>>>
>>> "brython" group.
>>> To unsubscribe from this group and stop receiving emails from it, send an
>>>
>>> email to brython+u...@googlegroups.com.
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/brython/2e35cf8d-3b02-4abc-9226-3a2592c64f20n%40googlegroups.com
>>>
>>> <https://groups.google.com/d/msgid/brython/2e35cf8d-3b02-4abc-9226-3a2592c64f20n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "brython" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to brython+u...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/brython/6d57b4e8-38b3-4b1e-a24b-070def1a886dn%40googlegroups.com.
>

Andy DʼArcy Jewell

unread,
Jan 19, 2024, 6:56:17 AMJan 19
to brython
Ok, thanks. Thanks to your hints, I've made some progress! I found an example on github. So now I have this as my click event handler, which is bound to my "Export" button in the HTML:

    def exprt(ev):
      docname = document.get(id=f"filename")[0].value
      if docname in storage:
        downloadable = window.JSON.stringify(storage[docname])
        blob = window.Blob.new( [downloadable] )
        url = window.URL.createObjectURL(blob)

I'm not using, or going to use Chrome, so I assume "chrome.downloads.download" isn't an option. I don't mind a dialog opening, so hopefully the final piece is just how to now trigger the download.

Ed

unread,
Jan 19, 2024, 7:14:16 AMJan 19
to bry...@googlegroups.com
chrome.downloads is an API for browser extensions. Most of chrome.* actually does work on Firefox, they copied the API for extension compatibility.  However it won’t be available in your case running from a normal web page. 

Sorry about the changing background colors on my previous code. Was copy pasting last night and didn’t notice (dark mode). 


Dave

unread,
Jan 20, 2024, 9:53:28 AMJan 20
to brython
If you are trying to write Android apps in Python/Brython then you might like to try the latest beta version of DroidScript which now includes Python support (via Brython).


You can access the native device features, including - native ui, storage, sdcard, bluetooth-serial, usb-serial, udp, tcp, etc by importing the app object like this - 

from native import app

def OnStart():
    app.WriteFile( "/Storage/myFile.txt", "Hello World" )


The latest docs also allow you to switch the samples code between JavaScript and Python by pressing the. 'JS' or 'Py' button (top right)


Note: There is a bug in this beta version preventing the use of the spaces in app names for Python apps (fixed in next release)

Dave

unread,
Jan 20, 2024, 10:06:38 AMJan 20
to brython
If you want to keep your current code the same, then you could drop it into a DroidScript 'HTML' app and then add the following snippet to get access to the hardware via the 'app' object - 

  <script>_python=true</script>
    <script src="ds:/Sys/app.js"></script>
    <script src="ds:/Sys/python/brython.min.js"> </script>
    <script src="ds:/Sys/python/brython_stdlib.min.js"> </script>

    <script type="text/python" id="native">
        import browser
        app = browser.window.app
    </script>

Andy DʼArcy Jewell

unread,
Jan 20, 2024, 12:32:38 PMJan 20
to brython
Thanks, but TBH I want to avoid the whole app building and side-loaded APK installation shenannigans, which is why I ended up going for Brython - it's just a webpage then, and it takes less than 5 minutes to put onto my web-server.

mana...@gmail.com

unread,
Jan 20, 2024, 4:50:45 PMJan 20
to bry...@googlegroups.com
Have a look at "htag"
It's a kind of brython on server side (so python) (it shares the same thing for building HTML tags)

It's a runner, to run an htag app, on Android.
(A really simple way to build an working apk)

There are a lot of others "runners" to run in an desktop app, web app ....

Thanks to brython which give me the idea to build this kind of libs...


Andy DʼArcy Jewell

unread,
Jan 21, 2024, 9:30:30 AMJan 21
to brython
Hi all, thanks for allyour input - I've now got it working! There might be a better way - please suggest away if you know one - and it's a lot easier than I'd expected.

My exprt click handler now looks like this:

>  def exprt(ev):
>    docname = document.get(id=f"filename")[0].value
>    if docname in storage:
>      downloadable = window.JSON.stringify(storage[docname])
>      blob = window.Blob.new( [downloadable] )
>      url = window.URL.createObjectURL(blob)
>      dl = document.get(id="download_link")[0]
>      dl.setAttribute("href",url)
>    else:
>      d1 = InfoDialog(f"Sheet {docname} does not exist", "Please try another name.", remove_after=3)

and the "supporting" html is:

>  <a id="download_link" href="" download="export.genrec">
>    <button type="button" id="export">Export</button>
>  </a>

When you click the button, the exprt function gets triggered, which "serialises" the local storage entry (which is already a string) is made into a blob, which a URL is created for; this is then stuffed into the href attrib of the anchor. The only issue left to resolve now is for the situation where the docname doesn't exist, where it currently STILL triggers a download, but I think that will just be a bit of judicious unwinding of the results of the above code, or maybe somehow "cancelling" the download.
Reply all
Reply to author
Forward
0 new messages