Розробка

Додаток на python kivy для різноманітності раціону харчування. Від коду і до отримання .apk файлу для Android


Вивчаю python kivy і для себе вирішив написав маленький додаток, щоб урізноманітнити своє харчування. Вирішив поділитися. Стаття розрахована на новачків в kivy. Додаток займає близько 100 рядків коду.

Мета створення велосипеда додатка:

1) Уникнути частих повторень в харчуванні. Щоб не вживати одне і те ж блюдо занадто часто.

2) Не забувати страви, які їв, потім забув і роками до них не повертався, тому що банально не пам’ятав. У мене таке буває.

Інтро

Можна не читати, інтро всяка лірика.

Довелося пожити в одній країні, в одному готелі, де годували щодня яйцями на сніданок і більше нічим, так через місяць почав свербіти. Звертався за медичною допомогою, хоча раніше ніколи нічим подібним не страждав. Навчений гірким досвідом після цього для себе вирішив допускати як можна менше повторень в їжі, щоб нічого не встигало в організмі накопичитися. Це мій особистий досвід, я просто розповідаю, без нав’язування. Можливо, немає правил без виключення, напевно, вівсянку можна їсти і кожен день протягом десятків років.

Скріншоти

Припустимо мій раціон складається з 50 страв. Наприклад, сьогодні їв омлет. Натискаю на кнопку, і омлет став на 50 рядок в черзі, а перед ним стоять 49 страв, які з’їм, щоб знову дістатися до омлету. Ось і вся логіка програми. (На скріншотах страви нагенеренные, всі збіги випадкові, на моєму реальному раціону відношення не мають).

Вихідний код і пояснення

from kivy.app App import
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.config import ConfigParser
from kivy.uix.textinput import TextInput
from kivy.uix.label Label import
from kivy.metrics import dp
from datetime import datetime
import os
import ast
import time


class MenuScreen(Screen):
 def __init__(self, **kw):
 super(MenuScreen, self).__init__(**kw)
 box = BoxLayout(orientation='vertical')
 box.add_widget(Button(text='Щоденник харчування', on_press=lambda x:
set_screen('list_food')))
 box.add_widget(Button(text='Додати блюдо в щоденник харчування',
 on_press=lambda x: set_screen('add_food')))
self.add_widget(box)


class SortedListFood(Screen):
 def __init__(self, **kw):
 super(SortedListFood, self).__init__(**kw)

 def on_enter(self): # Буде викликана в момент відкриття екрану

 self.layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
self.layout.bind(minimum_height=self.layout.setter('height'))
 back_button = Button(text='< Назад в головне меню',
 on_press=lambda x: set_screen('menu'),
 size_hint_y=None, height=dp(40))
self.layout.add_widget(back_button)
 root = RecycleView(size_hint=(1, None), size=(Window.width,
Window.height))
root.add_widget(self.layout)
self.add_widget(root)

 dic_foods = ast.literal_eval(
 App.get_running_app().config.get('General', 'user_data'))

 for f, d in sorted(dic_foods.items(), key=lambda x: x[1]):
 fd = f.decode('u8') + '' + (datetime.fromtimestamp(d).strftime('%Y-%m-%d'))
 btn = Button(text=fd, size_hint_y=None, height=dp(40))
self.layout.add_widget(btn)

 def on_leave(self): # Буде викликана в момент закриття екрану

 self.layout.clear_widgets() # очищаємо список


class AddFood(Screen):

 def buttonClicked(self, btn1):
 if not self.txt1.text:
return
 self.app = App.get_running_app()
 self.app.user_data = ast.literal_eval(
 self.app.config.get('General', 'user_data'))
 self.app.user_data[self.txt1.text.encode('u8')] = int(time.time())

 self.app.config.set('General', 'user_data', self.app.user_data)
self.app.config.write()

 text = "Останній доданий страва:" + self.txt1.text
 self.result.text = text
 self.txt1.text = "

 def __init__(self, **kw):
 super(AddFood, self).__init__(**kw)
 box = BoxLayout(orientation='vertical')
 back_button = Button(text='< Назад в головне меню', on_press=lambda x:
 set_screen('menu'), size_hint_y=None, height=dp(40))
box.add_widget(back_button)
 self.txt1 = TextInput(text=", multiline=False, height=dp(40),
 size_hint_y=None, hint_text="Назва страви")
box.add_widget(self.txt1)
 btn1 = Button(text="Додати страва", size_hint_y=None, height=dp(40))
btn1.bind(on_press=self.buttonClicked)
box.add_widget(btn1)
 self.result = Label(text=")
box.add_widget(self.result)
self.add_widget(box)


def set_screen(name_screen):
 sm.current = name_screen


sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SortedListFood(name='list_food'))
sm.add_widget(AddFood(name='add_food'))


class FoodOptionsApp(App):
 def __init__(self, **kvargs):
 super(FoodOptionsApp, self).__init__(**kvargs)
 self.config = ConfigParser()

 def build_config(self, config):
config.adddefaultsection('General')
 config.setdefault('General', 'user_data', '{}')

 def set_value_from_config(self):
 self.config.read(os.path.join(self.directory '%(appname)s.ini'))
 self.user_data = ast.literal_eval(self.config.get(
 'General', 'user_data'))

 def get_application_config(self):
 return super(FoodOptionsApp, self).get_application_config(
'{}/%(appname)s.ini'.format(self.directory))

 def build(self):
 return sm


if __name__ == '__main__':
FoodOptionsApp().run()

Я свідомо не використовував kv файли, так як код дано в навчальних цілях, для людей, які знайомі з мовою python. Все написано на голому python. В поясненнях я не буду зупинятися на поясненні python коду, а відразу перейду до специфічних фішках Kivy.
Поїхали:

  • class MenuScreen(Screen):

    Клас відповідає за запуск стартової сторінки додатка, його можна назвати як завгодно, наприклад StartScreen. І успадковує kivy модуль Screen. Програма складається з 3-х вікон, ось ці віконця і створюються за допомогою цього модуля

  • box = BoxLayout(orientation='vertical')

    BoxLayout ділить екран на рівні частини, за замовчуванням горизонтально, я написав orientation=’vertical’, щоб ділити вертикально

  • Button(text='Щоденник харчування', 
    on_press=lambda x: set_screen('list_food'))
    

    Button — створює кнопки, on_press задається, яка функція буде запущена при натисканні.

  • .add_widget()

    — додає кнопки в шари і вікна

  • self.layout = GridLayout(cols=1, spacing=10, size_hint_y=None)

    Grid Layout чимось нагадує тег table в html, вказується cols — кількість колонок або rows — кількість рядків.
    Можна вказувати обидва параметра або один параметр.
    Екран буде розділений на потрібну кількість відсіків.

  • root = RecycleView(size_hint=(1, None), size=(Window.width, Window.height))

    RecycleView — модуль, за допомогою якого створюється вертикальна прокрутка в моєму додатку. Особливість RecycleView в тому, що він будує скроли з елементами однакової ширини і висоти. І працює швидко. А є модуль ScrollView, він може будувати прокрутки з елементами різних розмірів, але працює повільніше, ніж RecycleView

  • config.get('General', 'user_data')

    — у коді часто зустрічаються такі рядки. Я просто в якості сховища даних використовував рідне сховище Config kivy. Ну, нехай буде кілька тисяч страв, немає сенсу городити город з sqlite і чимось подібним. Всі дані зберігаються в одному файлику. Зберігається цей файлик в тій же папці, що і сам додаток, якщо вказати self.directory як у моєму коді, але можна вказати self.user_data_dir, щоб цей файлик не знищувався при перестановці або оновлення.

Запуск на windows & linux & macos

Принцип для всіх операційних систем однаковий:

  1. Ставимо python3
  2. Ставимо kivy
  3. Створюємо файл main.py і встромляємо в нього цілком вищевказаний код
  4. Запускаємо командою
    python3 main.py

Програма повинна заробити.

Збірка apk файлу і запуск на телефоні андроїд

Отже, у нас є файл з кодом програми, написаний на python. Як тепер створити додаток, щоб його можна було запустити на телефоні з андроїдом? Раніше це був достатньо складний процес, що вимагає навичок і танців з бубном. Тепер це не проблема.
Ось покрокова інструкція:

  1. Викачуємо готову віртуальну машину від розробників kivy, у якої вже все відлагоджено. https://github.com/Zen-CODE/kivybits/blob/master/KivyCompleteVM/ReadMe.txt. Пароль: kivy
  2. Запускаємо її в Virtual Box.
  3. Відкриваємо термінал і вводимо наступні команди:
    # Ставимо останню версію python-for-android
    cd /home/kivy/Repos
    rm -fr python-for-android/
    git clone https://github.com/kivy/python-for-android.git
    cd ~
    mkdir Project
    cd Project
    git clone https://github.com/Alexmod/FoodOptions.git
    cd FoodOptions
    buildozer android debug
    # Перший раз ця команда буде довго тягнути 100500 всяких бібліотек,
    # але в наступні рази виконуватися за секунди. 
    
  4. Остання команда створює папку bin в тій же директорії, bin ви знайдете файл foodoptions-0.1-debug.апк, який можна закинути на телефон, встановити і насолоджуватися додатком

Як закинути apk файл на телефон?
Можна, звичайно, зробити це як завгодно, відправити собі на пошту, куди-небудь викласти, закинути в телеграм і т. д., а потім завантажити додаток на телефон.
Але існує спеціалізований інструмент для цього.
Включаємо на телефоні режим розробника, підключіть USB-кабелем. Виртуалка повинна побачити, що ви підключили телефон.
Далі встановлюємо adb:

sudo apt adb install

Після установки заходимо в папку bin і вводимо команду

adb install -r foodoptions-0.1-debug.апк 

І можна приблизно через хвилинку побачити на телефоні додаток після того, як побачимо
Success в консолі.

kivy@kivy-complete:~/Project/FoodOptions/bin$ adb install -r foodoptions-0.1-debug.апк 
342 KB/s (10083019 bytes in 28.730 s)
Success
kivy@kivy-complete:~/Project/FoodOptions/bin$ 

Якщо раптом додаток падає або веде себе не так, як очікувалося, то є ось така команда для перегляду помилок

adb logcat| grep python

Російське ім’я додатки
Якщо ви захочете, щоб ваш додаток називалося по-російськи, наприклад, «Щоденник харчування», то треба внести зміни у файл:

.buildozer/android/platform/build/dists/foodoptions/templates/strings.tmpl.xml

В тег appName прописується російська назва програми, ця папка створюється після першого запуску buildozer android debug. Після того як файл редагуєте, поверніться в папку FoodOptions і запустіть buildozer android debug повторно. Файл збереться по-новій. Після установки на телефон ім’я програми буде написано російською.

Про файл buildozer.spec
Ось мій файл з гитхаба: buildozer.spec
Саме цей файл вказує buildozer-у, як саме зібрати пакет.
Там безліч різних варіацій.
Кому цікаво, то введіть всередині виртуалки команду:

cd /tmp
buildozer init

Буде створено дефолтний файл buildozer.spec з купою коментарів і пояснень.
Наприклад, якщо ви хочете якусь свою іконку програми, то вказуєте
у рядку

icon.filename = %(source.dir)s/data/icon.png

свій файл з іконкою. І додаток збереться вже з вашої іконкою.
Якщо вам треба довантажити який-небудь специфічний модуль, який не входить в офіційну бібліотеку python, то це робиться у рядку вимога =
Загалом, розповідь про файл buildozer.spec може зайняти цілу статтю, а то й дві.

Завантаження програми в Google Play
Треба зарегаться, пройти всі процедури, отримати ключі.
І далі запускати

sudo apt install zipalign
buildozer android release
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore /path/keystore bin/apk-unsigned.apk apkname
zipalign -v 4 bin/apk-apkname-unsigned.apk bin/apk-apkname-release.апк

Отриманий файл apk-apkname-release.apk заливати в Google Play.

Посилання

 

  • Відео-уроки по kivy російською мовою. Особисто мені сподобалися
  • Статті на хабре про kivy від HeaTTheatR (і спасибі йому за допомогу!)

В принципі будь-яка людина, яка вміє програмувати на python, зможе змінити додаток і легко додати наступне:

  1. Додати дизайн, щоб додаток став красиве
  2. Використовувати kv-файли, щоб код став більш легким. Я б навів таку аналогію: ті, хто знайомий з веб-програмуванням, уявіть собі код без html темплейтів і з html темплейтами. Винос в kv-файли кнопок, верств і іншого — це щось на зразок jinja2 для веб-програміста. Логіка залишається .py файлах, а фенечки — kv-файли.
  3. Додати підрахунок калорій, білків, вуглеводів, жирів (БЖУ)
  4. Додати можливість фотографувати страви

Related Articles

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Close