![](https://habrastorage.org/getpro/habr/upload_files/066/988/c0b/066988c0baead2cc247e9dbfba3ce31a.jpeg)
Введение
В современном мире кроссплатформенность стала неотъемлемой частью разработки приложений. Однако, с таким разнообразием фреймворков, выбор подходящего инструмента для достижения этой цели может быть затруднительным.
Чтобы создать привлекательное мобильное приложение, которое будет отлично работать на Android и iOS, обычно требуется значительная доработка существующих инструментов, таких как Kivy или Tkinter. Именно здесь на сцену выходит Flet — фреймворк, который позволяет легко создавать веб-, десктопные и мобильные приложения, используя Flutter, популярный инструмент для создания пользовательских интерфейсов от Google, но на языке Python.
Давайте посмотрим, как создать базовое приложение-калькулятор с помощью Flet, и увидим, насколько простым и эффективным может быть этот фреймворк.
Кто такой этот Flet?
Flet — фреймворк, который позволяет создавать пользовательские интерфейсы непосредственно с помощью инструментария Flutter.
В чём его преимущества?
Он сочетает в себе простоту Python и богатые возможности Flutter по созданию интерфейсов, позволяя быстро разрабатывать кроссплатформенные приложения, не требуя большого опыта работы с фронтендом.
Для работы с этим инструментом не требуется SDK, а его функционал может быть легко расширен с помощью Flutter SDK.
❕Обратите внимание: для успешного освоения данной темы будет полезно иметь общее представление о некоторых ключевых концепциях фронтенда, таких как блочная модель, макеты Flexbox и Grid, а также о способах позиционирования элементов. Хотя вы можете продолжать работу без глубокого понимания этих тем, всё же настоятельно рекомендуется хотя бы немного с ними ознакомиться.
Давайте наконец создадим наш калькулятор!
Настраиваем окружение
Прежде чем приступить к написанию кода, убедитесь, что на вашей машине установлен Python. Затем выполните следующие шаги, чтобы настроить среду для Flet.
Устанавливаем Flet, например, при помощи pip. Открываем терминал или командную строку и вводим
pip install flet
Открываем любимый редактор кода (например, VSCode, Pycharm и т. д.) и создаём новый исполняемый Python-файл.
Давайте сначала проверим, всё ли работает, с помощью самой любимой фразы в сообществе разработчиков «Hello World!».
В нашем Python-файле вводим напишем код:
import flet as ft
def main(page: ft.Page):
page.add(ft.Text(value="Hello, World!"))
ft.app(target=main)
Запускаем и проверяем, всё ли работает. Если видим на экране надпись «Hello, World!», значит, мы готовы перейти к созданию нашего калькулятора.
![](https://habrastorage.org/getpro/habr/upload_files/ab9/ca7/514/ab9ca7514917b6bf1f04b1e024983624.png)
Создаём макет
Для начала займёмся структурой калькулятора. Используем виджет для создания колонок, которые будут выступать в роли дисплея и кнопок. Дисплей будет отображать текущий ввод, а кнопки позволят на них нажимать взаимодействовать с пользователем.
Напишем такой код:
from flet import (
app, Page, Container, Column, Row,
TextField, colors, border_radius, ElevatedButton, TextAlign, TextStyle
)
def main(page: Page):
page.title = "Calculator"
result = TextField(
hint_text='0', text_size=20,
color='white', text_align=TextAlign.RIGHT,
hint_style=TextStyle(
color=colors.WHITE, size=20
),
read_only=True
)
def button_click(e):
pass
button_row0 = Row(
[
ElevatedButton(text='C', on_click=button_click),
ElevatedButton(text='^', on_click=button_click),
ElevatedButton(text='%', on_click=button_click),
ElevatedButton(text='/', on_click=button_click),
]
)
button_row1 = Row(
[
ElevatedButton(text='7', on_click=button_click),
ElevatedButton(text='8', on_click=button_click),
ElevatedButton(text='9', on_click=button_click),
ElevatedButton(text='*', on_click=button_click),
]
)
button_row2 = Row(
[
ElevatedButton(text='4', on_click=button_click),
ElevatedButton(text='5', on_click=button_click),
ElevatedButton(text='6', on_click=button_click),
ElevatedButton(text='-', on_click=button_click),
]
)
button_row3 = Row(
[
ElevatedButton(text='1', on_click=button_click),
ElevatedButton(text='2', on_click=button_click),
ElevatedButton(text='3', on_click=button_click),
ElevatedButton(text='+', on_click=button_click),
]
)
button_row4 = Row(
[
ElevatedButton(text='0', on_click=button_click),
ElevatedButton(text='.', on_click=button_click),
ElevatedButton(text='=', on_click=button_click),
]
)
container = Container(
width=350, padding=20,
bgcolor=colors. BLACK,
content=Column(
[
result,
button_row0, button_row1, button_row2,
button_row3, button_row4
]
)
)
page.add(container)
if __name__ == '__main__':
app(target=main)
После исполнения этого кода мы увидим макет калькулятора, который пока будет выглядеть не очень хорошо, но это не страшно! Мы улучшим его, добавив некоторые интервалы, скругление контейнера и тему, чтобы наш калькулятор выглядел более аккуратным.
![](https://habrastorage.org/getpro/habr/upload_files/aaf/48c/8b9/aaf48c8b975d90dee6748272e266b007.png)
Объяснение кода
Вот мы создали макет, но некоторые из вас не поняли, как мы это сделали. Давайте разбираться!
В первой строке нашего кода мы импортируем необходимые элементы управления. Эти элементы, называемые виджетами Flet, играют ключевую роль в создании пользовательского интерфейса нашего приложения. В данном случае мы импортировали такие важные компоненты, как: app
, Page
, Container
, Column
, Row
, TextField
, colors
, border_radius
, ElevatedButton
, TextAlign
, TextStyle
.
Некоторые из них не являются полноценными виджетами, например, app
, colors
, border_radius
, TextAlign
и TextStyle
. Это классы и методы, которые добавляют дополнительные функции в наше приложение.
Например, app
позволяет нам запускать наше приложение в автономном режиме, ориентируясь на основной экземпляр. colors
дает возможность стилизовать наши элементы управления, поддерживающие атрибуты color
и bgcolor
, без необходимости определять их имена. А border_radius
дает возможность закруглять углы наших контейнеров.
В строке 7 мы определяем основной экземпляр нашего приложения как Page
. Страница — это контейнер, в котором располагаются элементы управления View
. Мы не будем углубляться в детали View, но можно ознакомиться с этим более подробно на официальном сайте.
Теперь мы установим заголовок нашей страницы, используя атрибут page.title
. Этот заголовок будет отображаться в строке заголовка нашего приложения.
В строках с 9 по 16 находится блок result
. Он обладает различными свойствами, но в этом проекте мы будем использовать лишь некоторые из них. Мы добавили текст «0», установили его размер равным 20, выбрали белый цвет и выровняли по правому краю. Кроме того, мы сделали этот текст недоступным для изменения. Это означает, что пользователи не смогут изменить его, используя клавиатуру.
В строке 18 мы определили обработчик события button_click
. В этой функции мы будем применять логику для работы нашего приложения, превращая его в настоящий калькулятор. Но пока там просто стоит pass
в качестве заглушки.
В строках с 21 по 59 мы определили наши строки, используя виджет Row
. Виджет Row
— это элемент управления, который отображает свои дочерние элементы в горизонтальном порядке, слева направо. Подобно линейному макету в разработке Android или линейным элементам в CSS, элемент управления Row
работает так же, выстраивая элементы управления по горизонтальной оси.
Затем мы добавили ElevatedButton
, который будет представлять кнопки в пользовательском интерфейсе калькулятора. Обратите внимание, что мы задали ему атрибуты text
и _onclick
. Атрибут text
определяет данные, которые будут отображаться на результатах при нажатии, а атрибут onclick
вызывает функцию button_click
для обработки событий.
Ещё у нас есть контейнер. Он помогает сделать элемент управления красивым. Можно выбрать цвет фона, интервалы, границы и их радиус. Ещё можно расположить элемент управления с помощью padding
, margin
и выравнивания.
Контейнер следует концепции бокс-модели, подобно той, что используется в CSS, как показано на рисунке ниже:
![](https://habrastorage.org/getpro/habr/upload_files/399/589/186/399589186bf734d7d7abef089c24a79a.png)
Элемент управления Column
работает как элемент управления Row
. Он упорядочивает свои дочерние элементы, как будто выстраивает их в ряд сверху вниз. Так мы можем удобно расположить кнопки в нужном порядке.
После того как мы определили элементы пользовательского интерфейса, нам необходимо отобразить их в нашем приложении и затем вызвать его. Для этого мы используем метод page.add()
, который позволяет нам добавлять и логически упорядочивать элементы пользовательского интерфейса.
Далее следует вызов нашего приложения в автономном режиме, что и было реализовано в строках 74-75.
Добавление функциональности
Обновите функцию нажатия на кнопку, чтобы она соответствовала приведенному ниже коду:
def button_click(e):
if e.control.text == "=":
try:
result.value = str(eval(result.value))
except Exception:
result.value = "Error"
elif e.control.text == "C":
result.value = ""
# elif e.control.text == "^":
# logic for powers
# pass
else:
result.value += e.control.text
result.update()
Объяснение кода
Давайте рассмотрим, что происходит в этом коде. Функция button_click
предназначена для обработки различных событий нажатия кнопок в нашем приложении-калькуляторе.
Вот краткое описание того, что в ней происходит:
Получение текста кнопки: Когда пользователь нажимает на кнопку, функция получает текст кнопки (например, '1', '2', '+', '-', 'C', '=') через e.control.text
. Это позволяет ей определить, с какой именно кнопкой взаимодействовал пользователь.
Очистка дисплея: При нажатии на кнопку 'C' ввод калькулятора очищается. Результат обнуляется, а в дисплее устанавливается 0. Калькулятор будет готов к новым расчётам.
Расчёт выражений: Если пользователь нажимает кнопку «=», калькулятор должен рассчитать текущее математическое выражение. Для этого мы используем функции str()
и eval()
, причем первая преобразует результат в строку, а вторая вычисляет его и выводит на дисплей. Если выражение недействительно, будет сгенерировано исключение, и вместо него появится сообщение «Error».
Остальные кнопки: Для остальных кнопок, таких как числа и операторы, функция добавляет текст кнопки на дисплей (который изначально равен «0» или очищается при нажатии на 'C'). Она заменяет «0» на значение кнопки, если это возможно, или добавляет его в конец дисплея/
После обработки нажатия кнопки страница обновляется с помощью метода page.update()
, чтобы отобразить новый ввод или результат на дисплее калькулятора. Каждый раз, когда вы нажимаете на кнопку и видите значение на дисплее или результат, именно этот метод page.update()
срабатывает.
?Примечание: Функция
eval()
может быть опасной, так как она выполняет любой код Python, а он может быть вредоносным. В более надёжном приложении лучше использовать другой, более безопасный способ.
Улучшение пользовательского интерфейса
Пока интерфейс у нас выглядит не так красиво, поэтому давайте его обновим и сделаем кнопки более привлекательными, используя следующий код:
button_row0 = Row(
[
ElevatedButton(text='C', expand=1, on_click=button_click,
bgcolor=colors.RED_ACCENT, color=colors.WHITE),
ElevatedButton(text='^', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
ElevatedButton(text='%', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
ElevatedButton(text='/', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
]
)
button_row1 = Row(
[
ElevatedButton(text='7', expand=1, on_click=button_click),
ElevatedButton(text='8', expand=1, on_click=button_click),
ElevatedButton(text='9', expand=1, on_click=button_click),
ElevatedButton(text='*', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
]
)
button_row2 = Row(
[
ElevatedButton(text='4', expand=1, on_click=button_click),
ElevatedButton(text='5', expand=1, on_click=button_click),
ElevatedButton(text='6', expand=1, on_click=button_click),
ElevatedButton(text='-', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100
),
]
)
button_row3 = Row(
[
ElevatedButton(text='1', expand=1, on_click=button_click),
ElevatedButton(text='2', expand=1, on_click=button_click),
ElevatedButton(text='3', expand=1, on_click=button_click),
ElevatedButton(text='+', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900),
]
)
button_row4 = Row(
[
ElevatedButton(text='0', expand=1, on_click=button_click),
ElevatedButton(text='.', expand=1, on_click=button_click),
ElevatedButton(
text='=', expand=2, on_click=button_click,
bgcolor=colors.GREEN_ACCENT, color=colors.AMBER
),
]
)
Что же именно мы изменили?
Для кнопок мы могли бы использовать атрибут width
, но это не дало бы желаемого результата и могло бы нарушить пользовательский интерфейс. Вы можете сами убедиться в этом, если попробуете.
Однако у нас есть альтернативный вариант — атрибут expand
. Он позволяет задавать значения только двух типов данных: Boolean
и int
.
Для обычных кнопок, таких как операторы, числа и кнопка очистки ввода, мы увеличили значение expand
на 1, а для кнопки равно — на 2.
Теперь о том, что делает атрибут expand
. Этот атрибут позволяет элементу управления заполнять свободное пространство в заданном контейнере. Таким образом, кнопки с expand
1 будут иметь одинаковую ширину, а кнопка "равно" расширится на 2, что означает, что ее размер будет равен ширине двух кнопок.
Обратите внимание, что мы добавили цвета и фоновые цвета к некоторым из наших кнопок, чтобы они выделялись на фоне кнопок с цифрами.
И ещё давайте добавим закругление в контейнер сразу поле атрибута padding так:border_radius=border_radius.all(20),
![](https://habrastorage.org/getpro/habr/upload_files/ade/ecf/721/adeecf721cb991c49b279e3cb0f1d927.png)
Теперь у вас есть полнофункциональный калькулятор, созданный с помощью Flet! Попробуйте донастроить его по своему вкусу или добавить дополнительные функции. Также можно упаковать его как отдельный APK, AAB для загрузки в Google Play Store или Apple App Store.
Вот полный код:
from flet import (
app, Page, Container, Column, Row,
TextField, colors, border_radius, ElevatedButton, TextAlign, TextStyle
)
from flet_core import ThemeMode
def main(page: Page):
page.title = "Calculator"
page.theme_mode = ThemeMode.DARK
page.horizontal_alignment = page.vertical_alignment = 'center'
result = TextField(
hint_text='0', text_size=20,
color='white', text_align=TextAlign.RIGHT,
hint_style=TextStyle(
color=colors.WHITE, size=20
),
read_only=True
)
def button_click(e):
if e.control.text == "=":
try:
result.value = str(eval(result.value))
except Exception:
result.value = "Error"
elif e.control.text == "C":
result.value = ""
# elif e.control.text == "^":
# logic for powers
# pass
else:
result.value += e.control.text
result.update()
button_row0 = Row(
[
ElevatedButton(text='C', expand=1, on_click=button_click,
bgcolor=colors.RED_ACCENT, color=colors.WHITE),
ElevatedButton(text='^', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
ElevatedButton(text='%', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
ElevatedButton(text='/', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
]
)
button_row1 = Row(
[
ElevatedButton(text='7', expand=1, on_click=button_click),
ElevatedButton(text='8', expand=1, on_click=button_click),
ElevatedButton(text='9', expand=1, on_click=button_click),
ElevatedButton(text='*', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900
),
]
)
button_row2 = Row(
[
ElevatedButton(text='4', expand=1, on_click=button_click),
ElevatedButton(text='5', expand=1, on_click=button_click),
ElevatedButton(text='6', expand=1, on_click=button_click),
ElevatedButton(text='-', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100
),
]
)
button_row3 = Row(
[
ElevatedButton(text='1', expand=1, on_click=button_click),
ElevatedButton(text='2', expand=1, on_click=button_click),
ElevatedButton(text='3', expand=1, on_click=button_click),
ElevatedButton(text='+', expand=1, on_click=button_click,
bgcolor=colors.BLUE_ACCENT_100,
color=colors.RED_900),
]
)
button_row4 = Row(
[
ElevatedButton(text='0', expand=1, on_click=button_click),
ElevatedButton(text='.', expand=1, on_click=button_click),
ElevatedButton(
text='=', expand=2, on_click=button_click,
bgcolor=colors.GREEN_ACCENT, color=colors.AMBER
),
]
)
container = Container(
width=350, padding=20,
bgcolor=colors.BLACK, border_radius=border_radius.all(20),
content=Column(
[
result,
button_row0, button_row1, button_row2,
button_row3, button_row4
]
)
)
page.add(container)
if __name__ == '__main__':
app(target=main)
? А если тебе интересны и другие полезные материалы по Python и IT, то можешь подписаться на мой канал в tg: PythonTalk ?
Комментарии (3)
Octabun
24.09.2024 08:20+5Также можно упаковать его как отдельный APK, AAB для загрузки в Google Play Store или Apple App Store.
Вот это самое интересное и ни разу не описанное.
как?
сколько весит?
сколько грузится?
сколько памяти?
нагрузка на процессор в покое?
А да, это же Translation, удивляться не приходится. Предложение: не попробовал- не переводи.
MainEditor0
24.09.2024 08:20Очень удобный, но жутко не эффективный способ разработки софта. Пробовал эту штуку, очень понравилась, очень просто, но какая-то желейность в нём почему-то чувствовалась, когда запускал приложение на Android.
unreal_undead2
Два года назад развлекался с написанием калькулятора на Питоне (смотрел на PyQt и tkinter). Сначала тоже делал классические кнопочки, потом понял, что проще в написании и удобнее в использовании - тупо строка для ввода, которая интерпретируется как питоновское выражение.