Ошибка Объект 'ControlEvent' не имеет атрибута 'local_x' при использовании flet

Пишу игру-кликер с помощью flet. При написании функции score_up (хочу чтобы на экране появлялось +1, где кликает пользователь) возникла ошибка:

    score_count.left = event.local_x
                       ^^^^^^^^^^^^^
    AttributeError: 'ControlEvent' object has no attribute 'local_x'
import asyncio
import flet as ft

async def main(page: ft.Page):
    page.title = "Привет"
    page.theme_mode = ft.ThemeMode.DARK
    page.bgcolor = "#141221"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.fonts = {"RobotoSlab": "https://github.com/google/fonts/raw/main/apache/robotoslab/RobotoSlab%5Bwght%5D.ttf"}
    page.theme = ft.Theme(font_family = "RobotoSlab")

    async def score_up(event: ft.ContainerTapEvent):
        score.data += 1
        score.value = str(score.data)

        image.scale = 0.95

        score_count.opacity = 50
        score_count.value = "+1"
        score_count.right = 0
        score_count.left = event.local_x
        score_count.top = event.local_y
        score_count.bottom = 0

        progress_bar.value += (1 / 100)

        await page.update_async()

        await asyncio.sleep(0.1)
        image.scale = 1
        score_count.opacity = 0

        await page.update_async()

    score = ft.Text(value="0", size=100, data=0)
    score_count = ft.Text(size=50, animate_opacity=ft.Animation(duration=600, curve=ft.AnimationCurve.BOUNCE_IN))
    image = ft.Image(src="cat.png", fit=ft.ImageFit.CONTAIN, animate_scale=ft.Animation(duration=600, curve=ft.AnimationCurve.EASE))
    progress_bar = ft.ProgressBar(value=0, width=page.width - 100, bar_height=20, color="#ff8b1f", bgcolor="#bf6524")
    
    await page.add_async(
        score,
        ft.Container(content=ft.Stack(controls=[image, score_count]), on_click=score_up, margin=ft.Margin(0, 0, 0, 30)),
        ft.Container(content=progress_bar, border_radius=ft.BorderRadius(10, 10, 10, 10))
    )

if __name__  == "__main__":
    ft.app(target = main, view = ft.WEB_BROWSER)

Искал решение в документации: нашёл про событие on_click, но должного результата это не дало


Ответы (1 шт):

Автор решения: Mantih

В итоге получилось. Разработчики забыли обновить документацию. Нужно делать через on_tap_down() вместе с on_click() (без него on_tap_down() работать не будет).

Вот что я сделал:

Добавил глобальную переменную tap_position для хранения координат нажатия:

tap_position = (0, 0)

Добавил функцию on_tap_down() для обработки события нажатия и обновления координат:

def on_tap_down(event: ft.ContainerTapEvent):
    global tap_position
    tap_position = (event.local_x, event.local_y)

Заменил:

score_count.left = event.local_x
score_count.top = event.local_y

на:

score_count.left = tap_position[0]
score_count.top = tap_position[1]

Добавил обработчик on_tap_down() в контейнер:

ft.Container(content=ft.Stack(controls=[image, score_count]), 
on_click=score_up, on_tap_down=on_tap_down, margin=ft.Margin(0, 0, 0, 30))

Полный код получился таким:

import asyncio
import flet as ft

async def main(page: ft.Page):
    page.title = "Привет"
    page.theme_mode = ft.ThemeMode.DARK
    page.bgcolor = "#141221"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.fonts = {"RobotoSlab": "https://github.com/google/fonts/raw/main/apache/robotoslab/RobotoSlab%5Bwght%5D.ttf"}
    page.theme = ft.Theme(font_family = "RobotoSlab")

    async def score_up(event: ft.ContainerTapEvent):
        score.data += 1
        score.value = str(score.data)

        image.scale = 0.95

        score_count.opacity = 50
        score_count.value = "+1"
        score_count.right = 0
        score_count.left = tap_position[0]
        score_count.top = tap_position[1]
        score_count.bottom = 0

        progress_bar.value += (1 / 100)

        await page.update_async()

        await asyncio.sleep(0.1)
        image.scale = 1
        score_count.opacity = 0

        await page.update_async()

    def on_tap_down(event: ft.ContainerTapEvent):
        global tap_position
        tap_position = (event.local_x, event.local_y)


    score = ft.Text(value="0", size=100, data=0)
    score_count = ft.Text(size=50, animate_opacity=ft.Animation(duration=600, curve=ft.AnimationCurve.BOUNCE_IN))
    image = ft.Image(src="cat.png", fit=ft.ImageFit.CONTAIN, animate_scale=ft.Animation(duration=600, curve=ft.AnimationCurve.EASE))
    progress_bar = ft.ProgressBar(value=0, width=page.width - 100, bar_height=20, color="#ff8b1f", bgcolor="#bf6524")
    
    await page.add_async(
        score,
        ft.Container(content=ft.Stack(controls=[image, score_count]), on_click=score_up, on_tap_down=on_tap_down, margin=ft.Margin(0, 0, 0, 30)),
        ft.Container(content=progress_bar, border_radius=ft.BorderRadius(10, 10, 10, 10))
    )

if __name__  == "__main__":
    tap_position = (0, 0)
    ft.app(target = main, view = ft.WEB_BROWSER)
→ Ссылка