Разработка мультисенсорных GUI на Python с использованием Kivy

Раздел: Разработка на Python -> графические интерфейсы

Основы работы с Kivy

Kivy представляет собой открытый фреймворк на Python для создания кроссплатформенных графических приложений, поддерживающих мультитач. Он использует собственный язык разметки KV, позволяющий отделить логику от представления. Рассмотрим наиболее эффективный подход к созданию интерфейсов с помощью Kivy, а также альтернативные варианты.

Как создать простое приложение с использованием KV language?

Наиболее эффективный способ разработки в Kivy – разделение логики (Python) и описания интерфейса (KV). Это упрощает поддержку и изменение внешнего вида.

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

class MainApp(App):
    def build(self):
        return Button(text='Привет, Kivy!')

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

Python gui (графические приложения python)

Однако лучше вынести интерфейс в KV файл. Создайте main.kv рядом с main.py:

# main.kv
Button:
    text: 'Привет, Kivy!'

Kivy python (фреймворк kivy)

Теперь Python код:

from kivy.app import App

class MainApp(App):
    pass

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

программа на python с графическим интерфейсом (программа с графическим интерфейсом на python)

Результат: окно с кнопкой, текст 'Привет, Kivy!'. При нажатии кнопка меняет цвет, но пока не обрабатывает событие.

Типичные ошибки: Имя KV файла должно совпадать с именем класса App (без 'App') в нижнем регистре. Например, для MainApp нужен main.kv. Если файл не найден, возникает ошибка KivyError. Также часто забывают импортировать нужные виджеты, хотя в KV они загружаются автоматически. Решение: проверять расположение и имя файла, использовать абсолютные пути или утилиту Builder.load_file.

Как создать интерфейс без использования KV языка?

Иногда требуется полностью динамическое создание компонентов. В этом случае применяется только Python.

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

class MyApp(App):
    def build(self):
        layout = BoxLayout(orientation='vertical')
        btn1 = Button(text='Кнопка 1')
        btn2 = Button(text='Кнопка 2')
        layout.add_widget(btn1)
        layout.add_widget(btn2)
        return layout

MyApp().run()

Цель использования: когда интерфейс строится на основе внешних данных или когда нет возможности использовать KV (например, в ограниченной среде).

Проблема: код становится громоздким при большом количестве виджетов. Решение: комбинировать подходы – сложную логику оставить в Python, а простые макеты – в KV.

Как расположить виджеты в определенном порядке с помощью макетов?

Kivy предоставляет несколько layout: BoxLayout, GridLayout, AnchorLayout, StackLayout и RelativeLayout. Пример с GridLayout:

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

class GridApp(App):
    def build(self):
        grid = GridLayout(cols=2, spacing=10, padding=10)
        grid.add_widget(Button(text='1'))
        grid.add_widget(Button(text='2'))
        grid.add_widget(Button(text='3'))
        grid.add_widget(Button(text='4'))
        return grid

GridApp().run()
Результат: таблица 2x2 с кнопками.

Вариант с AnchorLayout: привязывает виджет к одной из сторон или центру. Используется для элементов, которые не должны масштабироваться.

Ошибка: неправильный параметр cols/rows в GridLayout – если не установить cols, виджеты расположатся в один столбец. Решение: всегда явно указывать количество столбцов или строк.

Как реагировать на действия пользователя?

Обработка событий в Kivy осуществляется через привязку функций к сигналам виджетов, например on_press для кнопки.

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

class CallbackApp(App):
    def build(self):
        btn = Button(text='Нажми меня')
        btn.bind(on_press=self.on_button_press)
        return btn

    def on_button_press(self, instance):
        instance.text = 'Нажато!'

CallbackApp().run()

Можно также использовать метод on_touch_down для обработки касаний. Для кастомных виджетов переопределяются методы on_touch_down, on_touch_move и on_touch_up.

Проблема: забывают передать параметр instance в функцию обратного вызова. Это приводит к ошибке TypeError. Решение: убедиться, что сигнал передаёт объект виджета.

Как добавить анимацию элементов интерфейса?

Kivy включает мощный модуль анимации Animation. Пример плавного изменения прозрачности кнопки:

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

class AnimApp(App):
    def build(self):
        btn = Button(text='Анимированная кнопка')
        anim = Animation(opacity=0.2, duration=2) + Animation(opacity=1, duration=2)
        anim.repeat = True
        anim.start(btn)
        return btn

AnimApp().run()
Результат: кнопка плавно тускнеет и снова становится яркой бесконечно.

Цель: улучшение пользовательского опыта, привлечение внимания к элементам.

Типичная ошибка: не вызван start() у анимации. Анимация не запускается, но ошибки нет. Решение: всегда проверять, что анимация привязана к объекту.

Как рисовать графические примитивы с помощью Canvas?

Canvas в Kivy позволяет рисовать линии, круги, прямоугольники и другие фигуры непосредственно на виджете. Пример рисования круга:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Ellipse, Color

class CanvasWidget(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        with self.canvas:
            Color(1, 0, 0, 1)  # красный
            self.ellipse = Ellipse(pos=(50, 50), size=(100, 100))

class CanvasApp(App):
    def build(self):
        return CanvasWidget()

CanvasApp().run()
Результат: красный круг в нижней части окна.

Использование: создание кастомных элементов, графиков, игр.

Проблема: фигуры не обновляются при изменении свойств виджета. Решение: использовать bind или перерисовывать в методе update. Также часто забывают импортировать Color и Ellipse из kivy.graphics.

Как организовать несколько экранов в приложении?

Для навигации между экранами используется ScreenManager и Screen. Пример переключения между двумя экранами через кнопку:

from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button

class FirstScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.add_widget(Button(text='Перейти ко второму', on_press=self.go_to_second))

    def go_to_second(self, instance):
        self.manager.current = 'second'

class SecondScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.add_widget(Button(text='Назад', on_press=self.go_back))

    def go_back(self, instance):
        self.manager.current = 'first'

class ScreenApp(App):
    def build(self):
        sm = ScreenManager()
        sm.add_widget(FirstScreen(name='first'))
        sm.add_widget(SecondScreen(name='second'))
        return sm

ScreenApp().run()
Результат: первый экран с кнопкой, по нажатию появляется второй экран с кнопкой возврата.

Цель: создание многостраничных приложений, мастеров установки, мобильных приложений.

Ошибка: при добавлении экрана не указывается параметр name – переход по умолчанию не работает. Решение: всегда задавать уникальное имя для каждого экрана.

Расширенные примеры использования Kivy

Пример 1: приложение с вводом текста и кнопкой

Создадим простой интерфейс с полем ввода и кнопкой, при нажатии текст отображается в метке.

Пример
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.label import Label

class InputApp(App):
    def build(self):
        layout = BoxLayout(orientation='vertical', padding=20, spacing=10)
        self.text_input = TextInput(hint_text='Введите текст')
        self.label = Label(text='')
        btn = Button(text='Показать', on_press=self.show_text)
        layout.add_widget(self.text_input)
        layout.add_widget(btn)
        layout.add_widget(self.label)
        return layout

    def show_text(self, instance):
        self.label.text = self.text_input.text

InputApp().run()
Результат: в окне поле ввода, кнопка и метка. При вводе текста и нажатии кнопки текст копируется в метку.

Пример 2: анимация движения квадрата

Плавное перемещение квадрата по горизонтали.

Пример
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.animation import Animation

class MovingRect(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        with self.canvas:
            Color(0, 0, 1)
            self.rect = Rectangle(pos=(0, 100), size=(50, 50))
        self.animate()

    def animate(self):
        anim = Animation(pos=(self.width - 50, 100), duration=2)
        anim += Animation(pos=(0, 100), duration=2)
        anim.repeat = True
        anim.start(self.rect)

class MoveApp(App):
    def build(self):
        return MovingRect()

MoveApp().run()
Результат: синий квадрат движется от левого края к правому и обратно.

Пример 3: рисование линии пальцем

Реализация свободного рисования на виджете.

Пример
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line, Color
from kivy.vector import Vector

class PaintWidget(Widget):
    def on_touch_down(self, touch):
        with self.canvas:
            Color(0, 0, 0, 1)
            touch.ud['line'] = Line(points=(touch.x, touch.y), width=2)

    def on_touch_move(self, touch):
        if 'line' in touch.ud:
            touch.ud['line'].points += (touch.x, touch.y)

class PaintApp(App):
    def build(self):
        return PaintWidget()

PaintApp().run()
Результат: можно проводить пальцем (или мышью) и рисовать чёрные линии.

Пример 4: простые часы с обновлением каждую секунду

Используем Clock.schedule_interval для обновления метки.

Пример
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
import time

class ClockApp(App):
    def build(self):
        self.label = Label(text='')
        Clock.schedule_interval(self.update_time, 1)
        self.update_time()
        return self.label

    def update_time(self, dt=None):
        self.label.text = time.strftime('%H:%M:%S')

ClockApp().run()
Результат: метка показывает текущее время, обновляется каждую секунду.

Пример 5: загрузка данных из сети с помощью kivy.network.urlrequest

Пример получения данных с публичного API.

Пример
from kivy.app import App
from kivy.uix.label import Label
from kivy.network.urlrequest import UrlRequest

class NetApp(App):
    def build(self):
        self.label = Label(text='Загрузка...')
        req = UrlRequest('https://api.github.com/zen', on_success=self.on_success)
        return self.label

    def on_success(self, req, result):
        self.label.text = result

NetApp().run()
Результат: метка отображает случайную цитату из API GitHub (например, "Practicality beats purity.")

Пример 6: использование RecycleView для списка

RecycleView эффективно отображает большие списки.

Пример
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import ListProperty

class CustomLabel(RecycleDataViewBehavior, Label):
    pass

class RV(App):
    def build(self):
        rv = RecycleView()
        rv.data = [{'text': f'Элемент {i}'} for i in range(100)]
        rv.viewclass = 'CustomLabel'
        rv.recyclebox = rv
        return rv

RV().run()
Результат: вертикальный список из 100 элементов с прокруткой.

Пример 7: создание пользовательского виджета с помощью KV и Python

Создадим виджет, содержащий слайдер и метку для отображения значения.

Пример
# customwidget.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty

class CustomSlider(BoxLayout):
    value = NumericProperty(50)

    def on_value(self, instance, value):
        self.ids.label.text = f'Значение: {int(value)}'

class CustomApp(App):
    def build(self):
        return CustomSlider()

if __name__ == '__main__':
    CustomApp().run()
Пример
# customslider.kv
<CustomSlider>:
    orientation: 'vertical'
    padding: 20
    spacing: 10
    Slider:
        id: slider
        min: 0
        max: 100
        value: root.value
        on_value: root.on_value(self, self.value)
    Label:
        id: label
        text: 'Значение: ' + str(int(slider.value))
Результат: окно со слайдером и меткой, показывающей текущее значение. При перемещении ползунка значение обновляется.

фреймворк Kivy - comments

En
Kivy python (python)