You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flet应用报错AttributeError: 'ControlEvent'无productRow属性排查

问题:Flet POS软件中点击按钮添加容器时触发AttributeError错误

我正在开发一款基于Flet的POS软件,尝试实现点击按钮向ResponsiveRow中添加带指定信息的容器功能,但始终触发错误:AttributeError: 'ControlEvent' object has no attribute 'productRow'。该操作包含在向行发送容器模板的方法中,我参考了一个添加Checkbox的示例实现,但套用后出现问题。

参考示例代码

import flet as ft

class TodoApp(ft.UserControl):
    def build(self):
        self.new_task = ft.TextField(hint_text="Whats needs to be done?", expand=True)
        self.tasks = ft.Column()

        # application's root control (i.e. "view") containing all other controls
        return ft.Column(
            width=600,
            controls=[
                ft.Row(
                    controls=[
                        self.new_task,
                        ft.FloatingActionButton(icon=ft.icons.ADD, on_click=self.add_clicked),
                    ],
                ),
                self.tasks,
            ],
        )

    def add_clicked(self, e):
        self.tasks.controls.append(ft.Checkbox(label=self.new_task.value))
        self.new_task.value = ""
        self.update()


def main(page: ft.Page):
    page.title = "ToDo App"
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.update()

    # create application instance
    todo = TodoApp()

    # add application's root control to the page
    page.add(todo)

ft.app(target=main)

我的应用代码(VentaNa.py)

import flet as ft

class TopApp(ft.UserControl):

    def build(self):
        def close_addProduct(self):
            addProduct.open = False
            self.page.update()
            for i in range(3):
                NewProduct[i].value = ""

        def open_addProduct(self):
            self.page.dialog = addProduct
            addProduct.open = True
            self.page.update()

        def close_Search(self, nameProduct):
            self.text = nameProduct
            SearchProducts.close_view(self.text)

        NewProduct = [
            ft.TextField(label="Nombre"),
            ft.TextField(label="Precio"),
            ft.TextField(label="Imagen")
        ]

        addProduct = ft.AlertDialog(
            title=ft.Text("Agrega tus productos"),
            modal=True,
            content=ft.Column(
                NewProduct
            ),
            actions=[
                ft.TextButton(text="Cancelar", on_click=close_addProduct),
                ft.TextButton(text="Agregar", on_click=CenterApp.submittBttn, )
            ],
        )

        AddButton = ft.FloatingActionButton(
            icon=ft.icons.ADD, on_click=open_addProduct, bgcolor=ft.colors.LIGHT_BLUE_ACCENT_400
        )

        SearchProducts = ft.SearchBar(
            view_elevation=4,
            divider_color=ft.colors.LIGHT_BLUE_ACCENT_400,
            bar_hint_text="Buscador de productos",
            view_hint_text="Qué producto estas buscando...",
            width=900,
            on_change=None,
            on_submit=None,
            controls=[
                ft.ListTile(title=ft.Text(), on_click=close_Search)
            ],
        )
        return ft.Row(
            controls=[
                AddButton,
                SearchProducts
            ]
        )


class CenterApp(ft.UserControl):
    productRow = ft.ResponsiveRow(controls=[])

    def build(self):

        return ft.Column(
            width=600,
            controls=[self.productRow],  # Agrega el botón a los controles
        )

    def submittBttn(self, *args):  # Los controladores de eventos deben aceptar argumentos
        self.productRow.controls.append(ft.Container(
            content=ft.Column(
                [
                    ft.Text('hola')
                ]),
            margin=10,
            padding=10,
            alignment=ft.alignment.center,
            bgcolor=ft.colors.LIGHT_BLUE_ACCENT_400,
            width=200,
            height=200,
            border_radius=10,))

        self.update()

完整报错栈

Exception in thread Thread-79 (submittBttn):
Traceback (most recent call last):
  File "C:\Users\marct\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1073, in _bootstrap_inner
    self.run()
  File "C:\Users\marct\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1010, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\marct\OneDrive\Escritorio\CajaDeVenta\SellApp\VentaNa.py", line 75, in submittBttn
    self.productRow.controls.append(ft.Container(
    ^^^^^^^^^^^^^^^
AttributeError: 'ControlEvent' object has no attribute 'productRow'

主运行脚本

import flet as ft

import VentaNa


def main(page: ft.Page):

    page.title = 'Venta-Na'
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.window_min_width = 1270  # Minimum window width in pixels
    page.window_min_height = 800  # Minimum window height in pixels

    page.update()

    # Establecer la función de cambio de tamaño como el manejador del evento on_resize

    sell = VentaNa.TopApp()
    center = VentaNa.CenterApp()
    page.add(sell)
    page.add(center)


ft.app(target=main)

问题原因分析

  1. 事件绑定错误:在TopApp的弹窗按钮中,直接绑定了CenterApp.submittBttn类方法,而非CenterApp实例的方法。点击按钮时,Flet会将ControlEvent对象作为第一个参数传入,此时方法中的self是ControlEvent实例,而非CenterApp实例,因此找不到productRow属性。
  2. 类属性误用:productRow被定义为CenterApp的类属性,所有实例会共享同一行控件,不符合POS软件的独立实例需求。
  3. 跨实例访问问题:TopApp无法直接访问CenterApp实例,也无法获取弹窗输入框中的产品信息。
  4. 嵌套函数作用域混乱:TopApp中的嵌套函数使用self时,作用域指向不明确,可能引发其他错误。

修复方案及完整代码

修复后的VentaNa.py

import flet as ft

class TopApp(ft.UserControl):
    def __init__(self, center_app):
        super().__init__()
        self.center_app = center_app  # 保存CenterApp实例引用
        self.new_product_fields = []

    def build(self):
        self.new_product_fields = [
            ft.TextField(label="Nombre"),
            ft.TextField(label="Precio"),
            ft.TextField(label="Imagen")
        ]

        self.add_product_dialog = ft.AlertDialog(
            title=ft.Text("Agrega tus productos"),
            modal=True,
            content=ft.Column(self.new_product_fields),
            actions=[
                ft.TextButton(text="Cancelar", on_click=self.close_add_product),
                ft.TextButton(text="Agregar", on_click=self.submit_product)
            ],
        )

        add_button = ft.FloatingActionButton(
            icon=ft.icons.ADD, on_click=self.open_add_product, bgcolor=ft.colors.LIGHT_BLUE_ACCENT_400
        )

        search_bar = ft.SearchBar(
            view_elevation=4,
            divider_color=ft.colors.LIGHT_BLUE_ACCENT_400,
            bar_hint_text="Buscador de productos",
            view_hint_text="Qué producto estas buscando...",
            width=900,
            on_change=None,
            on_submit=None,
            controls=[
                ft.ListTile(title=ft.Text(), on_click=self.close_search)
            ],
        )
        return ft.Row(controls=[add_button, search_bar])

    def open_add_product(self, e):
        self.page.dialog = self.add_product_dialog
        self.add_product_dialog.open = True
        self.page.update()

    def close_add_product(self, e):
        self.add_product_dialog.open = False
        # 清空输入框
        for field in self.new_product_fields:
            field.value = ""
        self.page.update()

    def submit_product(self, e):
        # 获取输入值
        name = self.new_product_fields[0].value.strip()
        price = self.new_product_fields[1].value.strip()
        image_url = self.new_product_fields[2].value.strip()
        
        if name and price:  # 验证必填字段
            self.center_app.add_product_container(name, price, image_url)
            self.close_add_product(e)

    def close_search(self, e):
        search_text = e.control.title.value
        e.control.parent.close_view(search_text)


class CenterApp(ft.UserControl):
    def build(self):
        self.product_row = ft.ResponsiveRow(controls=[])  # 改为实例属性
        return ft.Column(width=600, controls=[self.product_row])

    def add_product_container(self, name, price, image_url):
        # 创建带产品信息的容器
        content = ft.Column([
            ft.Text(name, size=16, weight=ft.FontWeight.BOLD),
            ft.Text(f"Precio: ${price}", size=14),
        ])
        # 如果有图片URL,添加图片控件
        if image_url:
            content.controls.append(ft.Image(src=image_url, width=150, height=100, fit=ft.ImageFit.COVER))
        
        self.product_row.controls.append(ft.Container(
            content=content,
            margin=10,
            padding=10,
            alignment=ft.alignment.center,
            bgcolor=ft.colors.LIGHT_BLUE_ACCENT_400,
            width=200,
            height=250,
            border_radius=10,
        ))
        self.update()

修复后的主运行脚本

import flet as ft
import VentaNa

def main(page: ft.Page):
    page.title = 'Venta-Na'
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.window_min_width = 1270
    page.window_min_height = 800

    # 创建CenterApp实例,再传递给TopApp
    center_app = VentaNa.CenterApp()
    top_app = VentaNa.TopApp(center_app)
    
    page.add(top_app)
    page.add(center_app)

ft.app(target=main)

内容的提问来源于stack exchange,提问作者MarctHidalgo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:17:08