Файлы. Статические ресурсы. Файлы картинок.

Все файлы хранятся во внутренней папки приложения SimpleUI, которая полностью доступна из самого приложения, но недоступна для других приложений (кроме как через root). С файлами можно выполнять любые операции из обработчиков python - читать содержимое папки, читать файлы, записывать и т.д. Т.е. например, можно перехватить картинку, сделанную с камеры и выполнить кроп, сжатие в обработчике python а потом отправить на ресурс. Например, зная путь к файлу можно его открыть:

with open(filename, "rb") as image_file:
      image = image_file.read()

Каждой конфигарации выделена папка, равная UID-у этой конфигурации (который записан в объекте ConfigurationSettings), в которой по необходимости создаются 3 подпапки Data (для своих файлов), Downloads (для загрузок) и Datasets (для датасетов). Базы данных (Pelican, SQLite) размещаются в папках по умолчанию (без разделения на конфигурации), но их можно разместить в Data конфы, указав путь принудительно. Для Pelican в строке инициализации можно указать опциональный параметр data_folder если его поставить в True то БД Pelican будет размещаться в подпапке DataPelican

Для получения папки в SimpleUtilites (в pythonscript она уже импортирована как se) добавлены методы get_downloads_dir(), get_data_dir(), get_datasets_dir() также в pythonscript чтобы не утруждаться уже предзаполнены константы _data_dir и _downloads_dir. Также в SimpleUtilites есть метод delete_root_dir() для удаления всех папок конфигурации.

Диалоги открытия и сохранения файла из экрана

Открытие файла через диалог(пикер) состоит из комманды-переменной и обработчика события.

Подсказка

Можно открывать файлы и не через диалог, а через открытие файла приложением Simple через Открыть (доступен соответствующий общий обработчик).

OpenExternalFile , пустой параметр – команда запуска диалога, при открытии файла генерируется событие FileOpen которое можно перехватить в обработчике типа pythonbytes в который попадает байт-массив открытого файла.

Также доступно сохранение файла.

SaveExternalFile , параметр: {«path»:<путь к внутреннему файлу>,»default»:<имя по умолчанию>} – команда запуска диалога выбора локации сохранения файла и имени файла (можно выбрать имя по умолчанию). При успешном сохранении генерируется событие onInput listener = FileSave

Сохранение в Загрузки(Downloads)

В Java классе SimpleUtilites есть метод download_file(<имя файла>) которая сохраняет файл (сформированный в папке приложения) во внешнюю папку Downloads (коллекция Загрузки).

Таким образом можно экспортировать информацию в виде файлов.

Закачка больших файлов

В SimpleUtilites есть воркер для скачивания больших файлов. Он использует докачку, работает даже после перезапуска устройства (если не успел докачать), показывает прогресс и в конце кладет в папку Downloads конфигурации файл и вызывает событие. Метод download(String url,String user,String password,String filename,String postExecute) делает в общем все что нужно. Вот пример скачивания больших файлов с Яндекс-диска. Это сделано в pythonscript, а обработчик postExecute написан там же, а пакуется с помощью функции get_body

import requests
from urllib.parse import urlencode
from ru.travelfood.simple_ui import SimpleUtilites as su
import os

base_url = 'https://cloud-api.yandex.net/v1/disk/public/resources/download?'
public_key = 'https://disk.yandex.ru/d/U6YrMsXQmMbfOA'

# Получаем загрузочную ссылку
final_url = base_url + urlencode(dict(public_key=public_key))
response = requests.get(final_url)
download_url = response.json()['href']

# Вариант 1 - для маленьких файлов
#download_response = requests.get(download_url)
#with open(su.get_downloads_dir()+os.sep+'p_menu.txt', 'wb') as f:   # Здесь укажите нужный путь к  файлу
#    f.write(download_response.content)

# Вариант 2 - для больших файлов
def after_download_1():
    with open(hashMap.get("DownloadedFile"), encoding='utf-8-sig') as f:
        toast("Загрузили файл : "+hashMap.get("DownloadedFile"))
        hashMap.put("RestartScreen","")

postExecute = json_to_str([{"action": "run", "type":  "pythonscript","method":get_body(after_download_1) }])
su.download(download_url,None,None,'goods.txt',postExecute)

Сразу скажу, использование именно этого воркера не обязательно. Можно просто скачивать например requests и писать в файл, тем более что в симпле есть runasync и runprogress, но Гугл рекомендует воркеры. Также удобно тем, что можно стартануть сразу несколько закачек и видеть прогресс в шторке уведомлений.

Статические ресурсы

_images/mediafiles.PNG

Различные статические декорации (картинки, выводимые в экранах или меню, макеты html, небольшие библиотеки python) имеет смысл передавать вместе с конфигурацией. При загрузке конфигурации они сохраняются во временные файлы под определенным uuid, а для обращения к ним требуется ключ, указанный на закладке Медиафайлы

Если статический ресурс – картинка, то для вывода на форму достаточно указать ее ключ c префиксом ^ . Т.е. у элемента Картинка в «Заполнении поля» будет стоять ^pic1 , где pic1 – ключ картинки на закладке Медиафайлы

К любому статическому ресурсу можно обратиться по ключу, получить его абсолютное имя и далее делать с ним все что нужно:

from ru.travelfood.simple_ui import SimpleUtilites as suClass
filename = suClass.get_stored_file("key")

Изображения через файлы.

Файлы, генерируемые на устройстве, это в том числе изображения с камеры или медиагалереи устройства, загружаемые с ключом mm_local, работа с которыми описана в соответствующих разделах. У подобных файлов при сохранении генерируется uuid, по которому к нему можно обратиться. Этот uuid равен имени файла. Можно просто работать с именем файла, запоминая его в своих структурах данных. Например при фотографировании в режиме mm_local в переменную (допустим переменная камеры называется photo) сохраняется абсолютный путь с приставкой _path, то есть в данном случае будет переменная photo_path. Обращаться к файлам в контейнерах следует через ~<абсолютный путь к файлу> чтобы система поняла что вы передаете картинку в виде файла. Дело в том, что по умолчанию она ожидает base64 строку. По возможности используйте файлы, а не base64, особенно в карточках списков – это более производительно. Более подробнее это описано в графе «Режим работы с файлами изображений напрямую по абсолютному пути (через ~)» В пакет приложения включен Pillow которая дает возможность работать с картинками – ресайз и т.д. Вот пример открытия файла, и отправки его запросом

import json
import requests
import base64
from PIL import Image
import os
from pathlib import Path
from io import BytesIO
from requests.auth import HTTPBasicAuth

#...

filename="path_to_file.jpg"
if os.path.exists(filename):
      image = Image.open(filename)

      image.resize((500, 500))
      buffered = BytesIO()
      image.save(buffered, format="JPEG")
      img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')

      username=hashMap.get("WS_USER")
      password=hashMap.get("WS_PASS")
      url = hashMap.get("WS_URL")

      r = requests.post(url+'/data_upload', auth=HTTPBasicAuth(username, password,),
         headers={'Content-type': 'application/json', 'Accept':     'text/plain'},data=json.dumps({"file":filename,"base64":img_str}))

Приведенный выше пример передает содержимое в виде строки base64, однако в случае с файлами, особенно большими лучше использовать вариант с потоковой передачей, которую поддерживает requests:

with open('image-1.png', 'rb') as img_1:
    r = requests.post(url, auth=basic, data=img_1, headers=headers)

Временные файлы

Для удобства есть возможность генерировать временные файлы нужного расширения методом get_temp_file

from ru.travelfood.simple_ui import SimpleUtilites as subclass
output_file = suClass.get_temp_file("txt")