
Salam untuk semua penggemar dan pakar bahasa pemrograman Python!
Dalam artikel ini, saya akan menunjukkan kepada Anda bagaimana bekerja dengan animasi dalam kerangka Kivy lintas platform dalam hubungannya dengan perpustakaan komponen Desain Material Google - KivyMD . Kita akan melihat struktur proyek Kivy, menggunakan komponen material untuk membuat aplikasi seluler uji dengan beberapa animasi. Artikelnya tidak akan kecil dengan banyak animasi GIF, jadi tuangkan kopi dan ayo pergi!
Untuk membangkitkan minat pembaca, saya ingin segera menunjukkan hasil dari apa yang kami dapatkan pada akhirnya:

Jadi, untuk pekerjaan kita membutuhkan kerangka Kivy:
pip install kivy
Dan pustaka KivyMD, yang menyediakan widget Desain Material untuk kerangka Kivy:
pip install https://github.com/kivymd/KivyMD/archive/master.zip
Semuanya siap dimulai! Mari buka PyCharm dan buat proyek CallScreen baru dengan struktur direktori berikut:

Strukturnya bisa apa saja. Baik kerangka Kivy maupun pustaka KivyMD tidak memerlukan direktori apa pun yang diperlukan selain persyaratan standar - harus ada file bernama main.py di akar proyek . Ini adalah titik masuk ke aplikasi:

Di direktori data / gambar , saya telah menempatkan sumber daya grafis yang dibutuhkan aplikasi:
![]() |
![]() |
Di direktori uix / screens / baseclass , kita akan memiliki file callscreen.py dengan kelas Python dengan nama yang sama, di mana kita akan mengimplementasikan logika operasi layar aplikasi:

Dan di direktori uix / screens / kv , kita akan membuat file callscreen.kv (biarkan kosong untuk saat ini) - dengan deskripsi UI dalam bahasa DSL khusus Kivy Language :

Ketika proyek dibuat, kita dapat membuka file callscreen.py dan mengimplementasikan kelas layar aplikasi uji kita.
callscreen.py:
import os
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
# KV
with open(os.path.join(os.getcwd(), "uix", "screens", "kv", "callscreen.kv"), encoding="utf-8") as KV:
Builder.load_string(KV.read())
class CallScreen(MDScreen):
pass
Kelas CallScreen diwarisi dari widget MDScreen pustaka KivyMD (hampir semua komponen pustaka ini diawali dengan MD - Desain Material). MDScreen adalah analog dari widget Layar kerangka Kivy dari modul kivy.uix.screenmanager , tetapi dengan properti tambahan. MDScreen juga memungkinkan Anda untuk menempatkan widget dan pengontrol satu di atas yang lain dengan sendirinya sebagai berikut:

Ini adalah pemosisian yang akan kita gunakan saat menempatkan elemen mengambang di layar.
Pada titik masuk ke aplikasi - file main.py, buat kelas TestCallScreen , yang diwarisi dari kelas MDApp dengan metode build wajib , yang harus mengembalikan widget atau tata letak untuk menampilkannya di layar. Dalam kasus kami, ini akan menjadi kelas layar CallScreen yang dibuat sebelumnya .
main.py:
from kivymd.app import MDApp
from uix.screens.baseclass.callscreen import CallScreen
class TestCallScreen(MDApp):
def build(self):
return CallScreen()
TestCallScreen().run()
Ini adalah aplikasi siap pakai yang menampilkan layar kosong. Jika kita menjalankan file main.py , kita akan melihat:

Sekarang mari mulai menandai layar UI di file callscreen.kv . Untuk melakukan ini, Anda perlu membuat aturan dengan nama yang sama dengan kelas dasar, di mana kami akan menjelaskan widget dan propertinya. Misalnya, jika kita memiliki kelas Python yang disebut CallScreen , maka aturan dalam file KV harus memiliki nama yang sama persis. Meskipun Anda dapat membuat semua elemen antarmuka langsung di kode, ini, secara halus, tidak benar. Membandingkan:
MyRootWidget:
BoxLayout:
Button:
Button:
Dan analog Python:
root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)
Sangat jelas bahwa pohon widget jauh lebih mudah dibaca dalam Bahasa Kv daripada dalam kode Python. Selain itu, ketika widget memiliki argumen, kode Python Anda akan menjadi berantakan dan setelah satu hari Anda tidak akan dapat mengetahuinya. Oleh karena itu, siapa pun yang mengatakan apa pun, jika framework memungkinkan Anda mendeskripsikan elemen UI melalui bahasa deklaratif, ini adalah nilai tambah. Nah, di Kivy ini adalah nilai tambah ganda, karena di Kv Language Anda masih bisa menjalankan instruksi Python.
Jadi mari kita mulai dengan gambar judul:
callscreen.kv:
<CallScreen>
FitImage:
id: title_image # id
size_hint_y: .45 # (45% )
# root .
# <class 'uix.screens.baseclass.callscreen.CallScreen'>,
# self - - <kivymd.utils.fitimage.FitImage object>.
y: root.height - self.height # Y
source: "data/images/avatar.jpg" #
Widget FitImage secara otomatis diregangkan agar pas dengan seluruh ruang yang dialokasikan padanya sambil mempertahankan rasio aspek gambar:

Kita dapat menjalankan file main.py dan melihat hasilnya:

Untuk saat ini, semuanya sederhana dan saatnya mulai menganimasikan widget. Mari tambahkan tombol ke layar dengan menekan metode animasi dari kelas Python CallScreen : callscreen.kv akan dipanggil
:
#:import get_color_from_hex kivy.utils.get_color_from_hex
#:import colors kivymd.color_definitions.colors
<CallScreen>
FitImage:
[...]
MDFloatingActionButton:
icon: "phone"
x: root.width - self.width - dp(20)
y: app.root.height * 45 / 100 + self.height / 2
md_bg_color: get_color_from_hex(colors["Green"]["A700"])
on_release:
# .
root.animation_title_image(title_image); \
root.open_call_box = True if not root.open_call_box else False
Impor modul dalam Bahasa Kv:
#:import get_color_from_hex kivy.utils.get_color_from_hex
#:import colors kivymd.color_definitions.colors
Akan serupa dengan impor berikut dalam kode Python:
# get_color_from_hex
# rgba.
from kivy.utils import get_color_from_hex
# :
#
# colors = {
# "Red": {
# "50": "FFEBEE",
# "100": "FFCDD2",
# ...,
# },
# "Pink": {
# "50": "FCE4EC",
# "100": "F8BBD0",
# ...,
# },
# ...
# }
#
# https://kivymd.readthedocs.io/en/latest/themes/color-definitions/
from kivymd.color_definitions import colors

Setelah meluncurkan dan mengklik tombol hijau, kita mendapatkan - AttributeError: Objek 'CallScreen' tidak memiliki atribut 'animation_title_image' . Oleh karena itu, mari kita kembali ke kelas dasar CallScreen berkas callscreen.py dan menciptakan di dalamnya metode animation_title_image , yang akan menghidupkan judul gambar.
callscreen.py:
# .
from kivy.animation import Animation
[...]
class CallScreen(MDScreen):
# .
open_call_box = False
def animation_title_image(self, title_image):
"""
:type title_image: <kivymd.utils.fitimage.FitImage object>
"""
if not self.open_call_box:
# .
Animation(size_hint_y=1, d=0.6, t="in_out_quad").start(title_image)
else:
# .
Animation(size_hint_y=0.45, d=0.6, t="in_out_quad").start(title_image)
Seperti yang sudah Anda pahami, kelas Animation , mungkin, seperti di framework lain, hanya menganimasikan properti widget. Dalam kasus kami, kami akan menganimasikan properti size_hint_y - petunjuk ketinggian dengan menyetel interval eksekusi animasi dalam parameter d - durasi dan jenis animasi dalam parameter tipe - t . Kita dapat menganimasikan beberapa properti dari satu widget sekaligus, menggabungkan animasi menggunakan operator + , + = ... Gambar di bawah ini menunjukkan hasil pekerjaan kita. Sebagai perbandingan, untuk GIF yang tepat, saya menggunakan in_elastic dan out_elastic jenis animasi :
![]() |
![]() |
Langkah kita selanjutnya adalah menambahkan efek blur pada gambar judul. Untuk tujuan ini, Kivy memiliki EffectWidget . Kita perlu mengatur properti yang diinginkan untuk efek dan menempatkan widget gambar judul di EffectWidget.
callscreen.kv:
#:import effect kivy.uix.effectwidget.EffectWidget
#:import HorizontalBlurEffect kivy.uix.effectwidget.HorizontalBlurEffect
#:import VerticalBlurEffect kivy.uix.effectwidget.VerticalBlurEffect
<CallScreen>
EffectWidget:
effects:
# blur_value .
(\
HorizontalBlurEffect(size=root.blur_value), \
VerticalBlurEffect(size=root.blur_value), \
)
FitImage:
[...]
MDFloatingActionButton:
[...]
on_release:
# blur .
root.animation_blur_value(); \
[...]
Sekarang kita perlu menambahkan atribut blur_value ke kelas dasar Python CallScreen dan membuat metode animation_blur_value yang menganimasikan nilai efek blur.
callscreen.py:
from kivy.properties import NumericProperty
[...]
class CallScreen(MDScreen):
# EffectWidget.
blur_value = NumericProperty(0)
[...]
def animation_blur_value(self):
if not self.open_call_box:
Animation(blur_value=15, d=0.6, t="in_out_quad").start(self)
else:
Animation(blur_value=0, d=0.6, t="in_out_quad").start(self)
Hasil:

Perhatikan bahwa metode animasi akan dijalankan secara asinkron! Mari kita animasikan tombol panggil hijau agar tidak mengganggu mata kita.
callscreen.py:
from kivy.utils import get_color_from_hex
from kivy.core.window import Window
from kivymd.color_definitions import colors
[...]
class CallScreen(MDScreen):
[...]
def animation_call_button(self, call_button):
if not self.open_call_box:
Animation(
x=self.center_x - call_button.width / 2,
y=dp(40),
md_bg_color=get_color_from_hex(colors["Red"]["A700"]),
d=0.6,
t="in_out_quad",
).start(call_button)
else:
Animation(
y=Window.height * 45 / 100 + call_button.height / 2,
x=self.width - call_button.width - dp(20),
md_bg_color=get_color_from_hex(colors["Green"]["A700"]),
d=0.6,
t="in_out_quad",
).start(call_button)
callscreen.kv:
[...]
<CallScreen>
EffectWidget:
[...]
FitImage:
[...]
MDFloatingActionButton:
[...]
on_release:
# .
root.animation_call_button(self); \
[...]

Mari tambahkan dua item tipe TwoLineAvatarListItem ke layar utama.
callscreen.kv:
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
#:import IconLeftWidget kivymd.uix.list.IconLeftWidget
[...]
<ItemList@TwoLineAvatarListItem>
icon: ""
font_style: "Caption"
secondary_font_style: "Caption"
height: STANDARD_INCREMENT
IconLeftWidget:
icon: root.icon
<CallScreen>
EffectWidget:
[...]
FitImage:
[...]
MDBoxLayout:
id: list_box
orientation: "vertical"
adaptive_height: True
y: root.height * 45 / 100 - self.height / 2
ItemList:
icon: "phone"
text: "Phone"
secondary_text: "123 456 789"
ItemList:
icon: "mail"
text: "Email"
secondary_text: "kivydevelopment@gmail.com"
MDFloatingActionButton:
[...]
on_release:
root.animation_list_box(list_box); \
[...]

Kami membuat dua item ItemList dan menempatkannya dalam kotak vertikal. Kita dapat membuat metode baru animation_list_box di kelas CallScreen untuk menghidupkan kotak ini.
callscreen.py:
[...]
class CallScreen(MDScreen):
[...]
def animation_list_box(self, list_box):
if not self.open_call_box:
Animation(
y=-list_box.y,
opacity=0,
d=0.6,
t="in_out_quad"
).start(list_box)
else:
Animation(
y=self.height * 45 / 100 - list_box.height / 2,
opacity=1,
d=0.6,
t="in_out_quad",
).start(list_box)

Mari tambahkan toolbar ke layar.
callscreen.kv:
[...]
<CallScreen>
EffectWidget:
[...]
FitImage:
[...]
MDToolbar:
y: root.height - self.height - dp(20)
md_bg_color: 0, 0, 0, 0
opposite_colors: True
title: "Profile"
left_action_items: [["menu", lambda x: x]]
right_action_items: [["dots-vertical", lambda x: x]]
MDBoxLayout:
[...]
ItemList:
[...]
ItemList:
[...]
MDFloatingActionButton:
[...]

Avatar dan nama pengguna.
callscreen.kv:
[...]
<CallScreen>
EffectWidget:
[...]
FitImage:
[...]
MDToolbar:
[...]
MDFloatLayout:
id: round_avatar
size_hint: None, None
size: "105dp", "105dp"
md_bg_color: 1, 1, 1, 1
radius: [self.height / 2,]
y: root.height * 45 / 100 + self.height
x: root.center_x - (self.width + user_name.width + dp(20)) / 2
FitImage:
size_hint: None, None
size: "100dp", "100dp"
mipmap: True
source: "data/images/round-avatar.jpg"
radius: [self.height / 2,]
pos_hint: {"center_x": .5, "center_y": .5}
mipmap: True
MDLabel:
id: user_name
text: "Irene"
font_style: "H3"
bold: True
size_hint: None, None
-text_size: None, None
size: self.texture_size
theme_text_color: "Custom"
text_color: 1, 1, 1, 1
y: round_avatar.y + self.height / 2
x: round_avatar.x + round_avatar.width + dp(20)
MDBoxLayout:
[...]
ItemList:
[...]
ItemList:
[...]
MDFloatingActionButton:
root.animation_round_avatar(round_avatar, user_name); \
root.animation_user_name(round_avatar, user_name); \
[...]

Animasi khas dari posisi X dan Y dari avatar dan nama pengguna.
callscreen.py:
[...]
class CallScreen(MDScreen):
[...]
def animation_round_avatar(self, round_avatar, user_name):
if not self.open_call_box:
Animation(
x=self.center_x - round_avatar.width / 2,
y=round_avatar.y + dp(50),
d=0.6,
t="in_out_quad",
).start(round_avatar)
else:
Animation(
x=self.center_x - (round_avatar.width + user_name.width + dp(20)) / 2,
y=self.height * 45 / 100 + round_avatar.height,
d=0.6,
t="in_out_quad",
).start(round_avatar)
def animation_user_name(self, round_avatar, user_name):
if not self.open_call_box:
Animation(
x=self.center_x - user_name.width / 2,
y=user_name.y - STANDARD_INCREMENT,
d=0.6,
t="in_out_quad",
).start(self.ids.user_name)
else:
Animation(
x=round_avatar.x + STANDARD_INCREMENT,
y=round_avatar.center_y - user_name.height - dp(20),
d=0.6,
t="in_out_quad",
).start(user_name)

Kami hanya perlu membuat kotak dengan tombol:

Pada saat penulisan ini, saya menemukan fakta bahwa tombol yang diperlukan tidak ditemukan di perpustakaan KivyMD . Saya harus membuatnya sendiri dengan cepat. Saya hanya menambahkan instruksi kanvas ke kelas MDIconButton yang ada , mendefinisikan lingkaran di sekitar tombol, dan menempatkannya bersama dengan label dalam kotak vertikal. callscreen.kv:
<CallBoxButton@MDBoxLayout>
orientation: "vertical"
adaptive_size: True
spacing: "8dp"
icon: ""
text: ""
MDIconButton:
icon: root.icon
theme_text_color: "Custom"
text_color: 1, 1, 1, 1
canvas:
Color:
rgba: 1, 1, 1, 1
Line:
width: 1
circle:
(\
self.center_x, \
self.center_y, \
min(self.width, self.height) / 2, \
0, \
360, \
)
MDLabel:
text: root.text
size_hint_y: None
height: self.texture_size[1]
font_style: "Caption"
halign: "center"
theme_text_color: "Custom"
text_color: 1, 1, 1, 1
[...]

Selanjutnya, kami membuat kotak untuk menampung tombol khusus.
callscreen.kv:
<CallBox@MDGridLayout>
cols: 3
rows: 2
adaptive_size: True
spacing: "24dp"
CallBoxButton:
icon: "microphone-off"
text: "Mute"
CallBoxButton:
icon: "volume-high"
text: "Speaker"
CallBoxButton:
icon: "dialpad"
text: "Keypad"
CallBoxButton:
icon: "plus-circle"
text: "Add call"
CallBoxButton:
icon: "call-missed"
text: "Transfer"
CallBoxButton:
icon: "account"
text: "Contact"
[...]

Sekarang kita menempatkan CallBox yang dibuat dalam aturan CallScreen dan mengatur posisinya di sepanjang sumbu Y di luar batas bawah layar.
callscreen.kv:
[...]
<CallScreen>
EffectWidget:
[...]
FitImage:
[...]
MDToolbar:
[...]
MDFloatLayout:
[...]
FitImage:
[...]
MDLabel:
[...]
MDBoxLayout:
[...]
ItemList:
[...]
ItemList:
[...]
MDFloatingActionButton:
root.animation_call_box(call_box, user_name); \
[...]
CallBox:
id: call_box
pos_hint: {"center_x": .5}
y: -self.height
opacity: 0
Tetap hanya untuk menganimasikan posisi kotak yang dibuat dengan tombol.
callscreen.py:
from kivy.metrics import dp
[...]
class CallScreen(MDScreen):
[...]
def animation_call_box(self, call_box, user_name):
if not self.open_call_box:
Animation(
y=user_name.y - call_box.height - dp(100),
opacity=1,
d=0.6,
t="in_out_quad",
).start(call_box)
else:
Animation(
y=-call_box.height,
opacity=0,
d=0.6,
t="in_out_quad",
).start(call_box)

GIF akhir dengan tes di perangkat seluler:

Sekian, semoga bermanfaat!



