Разработка мобильных приложений на Python

 

 

 

 

 

                                                           Станислав Каблуков                           

Kivy

Как это работает?

  • Код на Python
  • Фреймворк Kivy
  • KV разметка 
  • Упаковка вместе с Сython
  • Генерация нативного bootstrap

Python

  • Python = 2.7 Python=3
  • cPython
  • PyGame

Kivy Framework

  • Canvas(PyGame, X11, SDL, EGL)
  • Native Events (HID, VM_Touch, Mac Touch ...)
  • Modules
  • GPU acceleration
  • RAW OpenGL support
  • MultiThread
  • OpenSource
  • MIT license
  • Properties
  • StringProperty
  • NumericProperty
  • ObjectProperty
  • DictProperty
  • ListProperty
  • BooleanProperty
  • ReferenceListProperty

Properties

Примеры

from kivy.properties import * 
from date time import date
				 
my_int = NumericProperty(42) 
my_string = StringProperty('My string') 
my_dict = DictProperty({'key': 'value'}) 
my_obj = ObjectProperty(date()) 
my_ref = referenceListProperty(
      my_int, my_string, my_dict, my_obj
 )

Layout System

  • BoxLayout
  • GridLayout
  • StackLayout
  • AnchorLayout
  • FloatLayout
 root = MyRootWidget()
                       
 box = BoxLayout()

 box.add_widget(Button())

 box.add_widget(Button())

 root.add_widget(box)

KV Language

 

 

KV Language

MyRootWidget:
    BoxLayout:
        Button:
        Button:

Это все еще Python

MyWidget:

    cols: len(some_data_from_python_file)

    on_size: my_callback_method

    pos: self.center_x / 2, self.center_y * 2

SOUNDBOARD

__version__ = '0.1'

import kivy
from kivy.lang import Builder
from kivy.uix.pagelayout import PageLayout


kivy.require('1.0.8')

from kivy.app import App
from kivy.uix.button import Button

from kivy.core.audio import SoundLoader
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from glob import glob
from os.path import dirname, join, basename
__version__ = '1.19.1'

import kivy
from kivy.uix.pagelayout import PageLayout

kivy.require('1.8.0')

from kivy.app import App
from kivy.uix.button import Button
from kivy.core.audio import SoundLoader
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from glob import glob
from os.path import dirname, join, basename
from kivy.uix.boxlayout import BoxLayout


class AudioButton(Button):

    filename = StringProperty(None)
    sound = ObjectProperty(None, allownone=True)
    volume = NumericProperty(1.0)

    def on_press(self):
        if self.sound is None:
            self.sound = SoundLoader.load(self.filename)
        # stop the sound if it's currently playing
        if self.sound.status != 'stop':
            self.sound.stop()
        self.sound.volume = self.volume
        self.sound.play()

    def release_audio(self):
        if self.sound:
            self.sound.stop()
            self.sound.unload()
            self.sound = None


class AudioBackground(PageLayout):
    pass


class DownBanner(BoxLayout):
    pass


class DownBannerToss(BoxLayout):
    pass


class DownBannerTerr(BoxLayout):
    pass

ZERG_LIST = []
TERRAN_LIST = []
PROTOSS_LIST = []


class AudioApp(App):

    def build(self):

        size = (80, 40)
        background_down = 'images/Knopka_hit.png'
        button_img = 'images/Knopka.png'
        size_hint = (0.2, 0.2)
        text_size = (None, None)
        green = [0, 1, 0, 1]

        # ROOT
        root = AudioBackground(spacing=5)

        # ==========================ZERG_BUTTON=========================================================================
        for cat in glob(join(dirname(__file__), "Sound", "Zerg", "*")):
            name_id = basename(cat).lower()

            ZERG_LIST.append(name_id)

            for fn in glob(join(cat, '*.wav')):
                btn = AudioButton\
                    (background_normal=button_img, color=green, border=[0, 0, 0, 0],
                     text=basename(fn[:-4]).replace('_', ' '), filename=fn,
                     size_hint=size_hint, halign='justify',
                     size=size, text_size=text_size, font_size=14, background_down=background_down)

                root.ids[name_id].add_widget(btn)

        # ==========================PROTOSS_BUTTON=====================================================================

        for cat in glob(join(dirname(__file__), "Sound", "Protoss", "*")):
            name_id = basename(cat).lower()

            PROTOSS_LIST.append(name_id)

            for fn in glob(join(cat, '*.wav')):
                btn = AudioButton(background_normal=button_img, color=green, border=[0, 0, 0, 0],
                                  text=basename(fn[:-4]).replace('_', ' '), filename=fn, size_hint=size_hint,
                                  halign='justify', size=size, text_size=text_size, background_down=background_down)

                if name_id == "advisor":
                    root.ids.advisorp.add_widget(btn)
                else:
                    root.ids[name_id].add_widget(btn)

        # ==========================Terran_BUTTON=====================================================================

        for cat in glob(join(dirname(__file__), "Sound", "Terran", "*")):
            name_id = basename(cat).lower()

            TERRAN_LIST.append(name_id)

            for fn in glob(join(cat, '*.wav')):
                btn = AudioButton(background_normal=button_img, color=green, border=[0, 0, 0, 0],
                                  text=basename(fn[:-4]).replace('_', ' '), filename=fn, size_hint=size_hint,
                                  halign='justify', size=size, text_size=text_size, background_down=background_down)

                if name_id == "advisor":
                    root.ids.advisort.add_widget(btn)
                else:
                    root.ids[name_id].add_widget(btn)

        return root

    def release_audio(self):

        for id in ZERG_LIST:
            for audiobutton in self.root.ids[id].children:
                audiobutton.release_audio()

    def release_audio_terr(self):

        for id in TERRAN_LIST:
            if id == "advisor":
                id = "advisort"

            for audiobutton in self.root.ids[id].children:
                audiobutton.release_audio()

    def release_audio_prot(self):

        for id in PROTOSS_LIST:
            if id == "advisor":
                id = "advisorp"
            for audiobutton in self.root.ids[id].children:
                audiobutton.release_audio()


if __name__ == '__main__':
    AudioApp().run()
class AudioButton(Button):

    filename = StringProperty(None)
    sound = ObjectProperty(None, allownone=True)
    volume = NumericProperty(1.0)

    def on_press(self):
        if self.sound is None:
            self.sound = SoundLoader.load(self.filename)
        # stop the sound if it's currently playing
        if self.sound.status != 'stop':
            self.sound.stop()
        self.sound.volume = self.volume
        self.sound.play()

    
#:kivy 1.8.0
#:import kivy kivy
<DownBanner>:
    padding: 10
    spacing: 10
    size_hint_x: 0.8
    size_hint_y: 0.2

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/icon.png'

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/sara.jpg'

    Image:
        size_hint: 0.5, 1
        size: 64, 64
        source: 'images/font/banner.bmp'

<DownBannerToss>:
    padding: 10
    spacing: 10
    size_hint_x: 0.8
    size_hint_y: 0.2

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/zeal.png'

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/zeal2.png'

    Image:
        size_hint: 0.5, 1
        size: 64, 64
        source: 'images/font/banner.bmp'

<DownBannerTerr>:
    padding: 10
    spacing: 10
    size_hint_x: 0.8
    size_hint_y: 0.2

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/wrath.png'

    Image:
        size_hint: 0.2, 1
        size: 64, 64
        source: 'images/marine.png'

    Image:
        size_hint: 0.5, 1
        size: 64, 64
        source: 'images/font/banner.bmp'

<AudioBackground>:

    BoxLayout:
        canvas:
            Color:
                rgb: 1, 1, 1
            Rectangle:
                pos: self.pos
                source: 'images/fon/zerus.jpg'
                size: self.size

        orientation: 'vertical'

        BoxLayout:
            size_hint_x: 1
            size_hint_y: 0.2

            ToggleButton:
                text: 'Loop'
                size_hint_x: 0.4
                size_hint_y: 1
                id: btnloop
                background_normal: "images/loop.png"
                background_down: "images/loop_hit.png"
                color: (0, 0, 0, 1)

            Button:
                text: 'Stop'
                size_hint: 0.2, 1
                background_normal: "images/stop.png"
                on_press: app.release_audio()
                color: (0, 1, 0, 1)

            Button:
                size_hint_x: 0.1
                size_hint_y: 1
                text: 'Prev'
                background_normal: "images/next.png"
                on_release: carousel.load_previous()
                color: (0, 1, 0, 1)

            Image:
                size_hint: 0.1, 1
                size: 64, 64
                source: 'images/zerg/{}.gif'.format(carousel.index)

            Button:
                size_hint: 0.1, 1
                text: 'Next'
                background_normal: "images/next.png"
                on_release: carousel.load_next()
                color: (0, 1, 0, 1)

        Carousel:
            id: carousel
            size_hint_x: 1
            size_hint_y: 0.6
            loop: btnloop.state == 'down'

            StackLayout:
                id: advisor

            StackLayout:
                id: avenger

            StackLayout:
                id: broodling

            StackLayout:
                id: bugguy

            StackLayout:
                id: defiler

            StackLayout:
                id: devourer

            StackLayout:
                id: drone

            StackLayout:
                id: egg

            StackLayout:
                id: guardian

            StackLayout:
                id: hydra

            StackLayout:
                id: larva

            StackLayout:
                id: lurker

            StackLayout:
                id: mutalid

            StackLayout:
                id: overlord

            StackLayout:
                id: queen

            StackLayout:
                id: ultra

            StackLayout:
                id: zergling

        DownBanner:

    BoxLayout:
        canvas:
            Color:
                rgb: 1, 1, 1
            Rectangle:
                pos: self.pos
                source: 'images/fon/background.jpg'
                size: self.size

        orientation: 'vertical'

        BoxLayout:
            size_hint_x: 1
            size_hint_y: 0.2
            ToggleButton:
                text: 'Loop'
                size_hint: 0.4, 1
                id: btnloops
                background_normal: "images/loop.png"
                background_down: "images/loop_hit.png"
                color: (0, 0, 0, 1)

            Button:
                text: 'Stop'
                size_hint_x: None
                size_hint: 0.2, 1
                background_normal: "images/stop.png"
                on_press: app.release_audio_prot()
                color: (0, 1, 0, 1)

            Button:
                size_hint_x: 0.1
                size_hint_y: 1
                text: 'Prev'
                background_normal: "images/next.png"
                on_release: carousels.load_previous()
                color: (0, 1, 0, 1)

            Image:
                size_hint: 0.1, 1
                size: 64, 64
                source: 'images/protoss/{}.gif'.format(carousels.index)

            Button:
                size_hint: 0.1, 1
                text: 'Next'
                background_normal: "images/next.png"
                on_release: carousels.load_next()
                color: (0, 1, 0, 1)

        Carousel:
            id: carousels
            size_hint_x: 1
            size_hint_y: 0.6
            loop: btnloops.state == 'down'

            StackLayout:
                id: advisorp

            StackLayout:
                id: arbiter

            StackLayout:
                id: archon

            StackLayout:
                id: carrier

            StackLayout:
                id: corsair

            StackLayout:
                id: darchon

            StackLayout:
                id: darktemplar

            StackLayout:
                id: dragoon

            StackLayout:
                id: intercep

            StackLayout:
                id: probe

            StackLayout:
                id: scout

            StackLayout:
                id: shuttle

            StackLayout:
                id: templar

            StackLayout:
                id: trilobyte

            StackLayout:
                id: witness

            StackLayout:
                id: zealot

        DownBannerToss:


    BoxLayout:
        canvas:
            Color:
                rgb: 1, 1, 1
            Rectangle:
                pos: self.pos
                source: 'images/fon/terra.jpg'
                size: self.size

        orientation: 'vertical'

        StackLayout:
            size_hint: 1, 0.2

            ToggleButton:
                text: 'Loop'
                id: btnloopt
                size_hint: 0.4, 1
                background_normal: "images/loop.png"
                background_down: "images/loop_hit.png"
                color: (0, 0, 0, 1)

            Button:
                text: 'Stop'
                size_hint: 0.2, 1
                on_press: app.release_audio_terr()
                background_normal: "images/stop.png"
                color: (0, 1, 0, 1)

            Button:
                size_hint_x: 0.1
                size_hint_y: 1
                text: 'Prev'
                background_normal: "images/next.png"
                on_release: carouselt.load_previous()
                color: (0, 1, 0, 1)

            Image:
                size_hint: 0.1, 1
                size: 64, 64
                source: 'images/terran/{}.gif'.format(carouselt.index)

            Button:
                size_hint: 0.1, 1
                text: 'Next'
                background_normal: "images/next.png"
                on_release: carouselt.load_next()
                color: (0, 1, 0, 1)

        Carousel:
            id: carouselt
            size_hint_x: 1
            size_hint_y: 0.6
            loop: btnloopt.state == 'down'

            StackLayout:
                id: advisort

            StackLayout:
                id: battle

            StackLayout:
                id: civilian

            StackLayout:
                id: dropship

            StackLayout:
                id: firebat

            StackLayout:
                id: frigate

            StackLayout:
                id: ghost

            StackLayout:
                id: goliath

            StackLayout:
                id: marine

            StackLayout:
                id: medic

            StackLayout:
                id: phoenix

            StackLayout:
                id: scv

            StackLayout:
                id: tank

            StackLayout:
                id: vessel

            StackLayout:
                id: vulture

        DownBannerTerr:

Build and distribute

 

PyInstaller (Windows, OS X)

pip(Linux)

KV tool/ Python for android/ Buildozer(Android)

Build and distribute... iOS

Build and distribute... iOS

1.Compile Python and Modules 

2. Create Xcode project

3. Insert your code in project

4. Link Python and modules

5. Customize (title, icons, etc)

6. Try build

Links

  • http://kivy.org
  • https://github.com/kivy

Kivy

By Станислав