[Python] How to stop updating a control during route change

Discussão em 'Python' iniciado por Stack, Março 10, 2025.

  1. Stack

    Stack Membro Participativo

    I developed a simple app using Python Flet 0.21.2. The app consists into two main views: the 'Welcome' and the 'Login', each of them defined into separate classes. Routing occurs into the main() function, in the route_change event, in which the 'views_manager()' is called. The 'Welcome' view implements a simple UI, with a container, a progress bar and a button defined into the build method. According to the user control lifecycle documentation, the did_mount() event occurs after the control is added to the page, whilst the will_unmount() event occurs before the control is removed from the page. So, in order to update the layout, which consists of changing the controls color, the update_layout() method is called into the did_mount() event. In the will_unmount() the value of a boolean variable is changed in order to stop the layout updating. Here is the full code

    import flet as ft
    import time

    def views_manager(page):
    # returns a dictionary of views with keys corresponding to their routes
    return {
    '/' : Welcome(
    parent_page=page,
    route='/',
    on_click=lambda _: page.go('/login')
    ),
    '/login' : Login(
    parent_page=page,
    route='login',
    on_click=lambda _: page.go('/')
    ),
    }


    class Welcome(ft.View):
    def __init__(self, parent_page, route, on_click):
    super().__init__()
    self.padding = 0
    self.parent_page = parent_page
    self.route = route
    self.login_on_click = on_click
    self.updating = None


    def update_layout(self):
    """
    Updates the layout by incrementally changing the progress bar value and updating the background color of the
    top container and the login button every 10% of progress.

    This function iterates from 0 to 100, updating the progress bar's value and sleeping for 0.05 seconds
    between each increment. When the progress reaches a multiple of 10, it changes the background color of
    the top container and the login button based on a predefined list of green shades. After reaching 100%,
    it resets the progress.
    """
    colors = [ft.colors.GREEN_50, ft.colors.GREEN_100, ft.colors.GREEN_200, ft.colors.GREEN_300, ft.colors.GREEN_400, ft.colors.GREEN_500, ft.colors.GREEN_600, ft.colors.GREEN_700, ft.colors.GREEN_800, ft.colors.GREEN_900]

    val=0
    while val < 101:
    if not self.updating:
    break
    else:
    #if self.pb in self.controls:
    self.pb.value = val * 0.01
    #update container bgcolor every 10%
    mod = val % 10
    if mod == 0.0:
    self.topContainer.bgcolor = colors[int(val/10) - 1]
    self.loginButton.style = ft.ButtonStyle(bgcolor=colors[int(val/10) - 1])
    #update val value
    val += 1
    if val == 100:
    val=0
    #update the page
    time.sleep(0.05)
    self.update()

    def did_mount(self):
    self.updating = True
    self.update_layout()

    def will_unmount(self):
    self.updating = False

    def build(self):
    self.topContainer = ft.Container(
    bgcolor=ft.colors.GREEN,
    width=self.parent_page.window_width,
    height=self.parent_page.window_height * 0.25,
    )
    self.pb = ft.ProgressBar()
    self.loginButton=ft.FilledButton(text="LOGIN", on_click = self.login_on_click)

    return ft.Column(
    controls=[
    self.topContainer,
    self.pb,
    ft.Row(
    [
    ft.Container(
    content=self.loginButton,
    padding=ft.padding.only(top=120),
    )
    ],
    alignment="center",
    vertical_alignment="end",
    )
    ],
    spacing=0,
    )


    class Login(ft.View):
    def __init__(self, parent_page, route, on_click):
    super().__init__()
    self.parent_page = parent_page
    self.route = route
    self.back_to_main = on_click

    def build(self):
    return ft.Container(
    content = ft.IconButton(
    icon=ft.icons.ARROW_BACK, icon_size=30,
    on_click=self.back_to_main,
    ),
    )


    def main(page: ft.Page):
    def route_change(route):
    page.views.clear()
    page.views.append(
    # returns a dictionary of views with keys corresponding to their routes
    views_manager(page)[page.route]
    )
    page.update()

    page.theme = ft.Theme(color_scheme_seed = ft.colors.GREEN)
    page.theme_mode = "light"
    page.horizontal_alignment = "center"
    page.vertical_alignment = "center"
    page.window_height = 700
    page.window_width = 400
    page.window_left = 990
    page.window_resizable = False
    page.window_maximizable = False

    page.on_route_change = route_change
    page.go("/")

    ft.app(target=main, assets_dir="assets")


    When the user clicks on the button, this error occurs:

    Future exception was never retrieved
    future: <Future finished exception=AssertionError('Control must be added to the page first.')>
    Traceback (most recent call last):
    File "C:\Users\idoec\miniconda3\Lib\concurrent\futures\thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "C:\Users\idoec\Desktop\main.py", line 120, in route_change
    page.update()
    File "C:\Users\idoec\miniconda3\Lib\site-packages\flet_core\page.py", line 303, in update
    self.__handle_mount_unmount(*r)
    File "C:\Users\idoec\miniconda3\Lib\site-packages\flet_core\page.py", line 448, in __handle_mount_unmount
    ctrl.did_mount()
    File "C:\Users\idoec\Desktop\main.py", line 64, in did_mount
    self.update_layout()
    File "C:\Users\idoec\Desktop\main.py", line 60, in update_layout
    self.update()
    File "C:\Users\idoec\miniconda3\Lib\site-packages\flet_core\control.py", line 286, in update
    assert self.__page, "Control must be added to the page first."
    AssertionError: Control must be added to the page first.


    How can I stop the layout update when the 'Login' view is loaded?

    Continue reading...

Compartilhe esta Página