Skip to content

bug: ft. Container component cannot be resized #6466

@Qye-Leisure

Description

@Qye-Leisure

Duplicate Check

Describe the bug

I originally wanted to make an infinite canvas, I have implemented the canvas drag, zoom, offset and other functions, and the last function is missing, that is, to expand the size of the canvas, but I found that when I used the method of self.canvas_container.width=xxx, I found that it was not feasible when I changed the size, why is that? , I remember that there was no bug in the previous version

Code sample

Code
import flet as ft
from typing import Any, Optional, cast, Callable
import asyncio
from dataclasses import field


@ft.control
class BaseCanvas(ft.GestureDetector):
    max_scale: float = 3
    scale_step: float = 0.25
    min_scale: float = 0.25

    def init(self):
        self.cumulative_offset_x = self.cumulative_offset_y = 0
        self.current_scale = 1

        self.window_container_size = None
        self.scaled_canvas_size = ()
        self.scale_offset_x = 0
        self.scale_offset_y = 0

        self.canvas_container = ft.Container(
            bgcolor=ft.Colors.BLUE_100,
            content=ft.Stack(
                controls=[
                    self.module_container((50, 50)),
                    self.module_container((50, 600)),
                    self.module_container((1000, 50)),
                    self.module_container((1000, 600)),
                ],
            ),
        )

        # self.content = ft.Container(
        #     expand=True,
        #     bgcolor=ft.Colors.AMBER,
        #     clip_behavior=ft.ClipBehavior.HARD_EDGE,
        #     content=self.canvas_container,
        #     on_size_change=lambda e: self.get_window_container_size(e),
        # )

        # ft.Stack
        self.content = ft.Container(
            expand=True,
            bgcolor=ft.Colors.AMBER,
            clip_behavior=ft.ClipBehavior.HARD_EDGE,
            content=ft.Stack([self.canvas_container]),
            on_size_change=lambda e: self.get_window_container_size(e),
        )

        self.on_scroll = self.canvas_zoom_event
        self.on_pan_update = self.drag_canvas_event
        self.on_hover = self.mouse_hover_event

    def module_container(self, pos):
        module = ft.Container(
            bgcolor=ft.Colors.GREEN,
            width=70,
            content=ft.Column(
                [
                    ft.Container(width=70, content=ft.Text(f"项目 {j}"))
                    for j in range(10)
                ]
            ),
        )

        return ft.GestureDetector(
            mouse_cursor=ft.MouseCursor.MOVE,
            drag_interval=5,
            left=pos[0],
            top=pos[1],
            content=module,
        )

    def get_window_container_size(self, e):

        self.window_container_size = (e.width, e.height)
        print(self.window_container_size)

    def canvas_zoom_event(self, e: ft.ScrollEvent[ft.GestureDetector]):
        """Zoom centered on mouse position"""

        canvas_width, canvas_height = (
            self.window_container_size if self.window_container_size else (500, 600)
        )

        x_center = canvas_width / 2
        y_center = canvas_height / 2

        x = (self.mouse_pos_canvas[0] - x_center) / x_center
        y = (self.mouse_pos_canvas[1] - y_center) / y_center

        if e.scroll_delta.y > 0:  # zoom in
            if self.current_scale + self.scale_step <= self.max_scale:
                self.current_scale += self.scale_step
            else:
                return
        else:  # zoom out
            if self.current_scale - self.scale_step >= self.min_scale:
                self.current_scale -= self.scale_step
            else:
                return

        self.canvas_container.scale = ft.Scale(
            alignment=ft.Alignment(x, y), scale=self.current_scale
        )
        self.scale_offset_x = (self.current_scale - 1) * (
            x_center - self.mouse_pos_canvas[0]
        )
        self.scale_offset_y = (self.current_scale - 1) * (
            y_center - self.mouse_pos_canvas[1]
        )

        mouse_container_x, mouse_container_y = self.mouse_pos_container
        mouse_canvas_x, mouse_canvas_y = self.mouse_pos_canvas

        self.cumulative_offset_x = (
            mouse_container_x - mouse_canvas_x
        ) / canvas_width
        self.cumulative_offset_y = (
            mouse_container_y - mouse_canvas_y
        ) / canvas_height

        self.canvas_container.offset = ft.Offset(
            self.cumulative_offset_x, self.cumulative_offset_y
        )
        self.canvas_container.update()

        print(f"当前缩放级别:{self.current_scale}")
        print(f"当前缩放中心:{(x, y)}")
        print(f"当前缩放偏移:{self.scale_offset_x, self.scale_offset_y}")

    def drag_canvas_event(self, e: ft.DragUpdateEvent[ft.GestureDetector]):
        """Handle canvas dragging, update offset"""
        if e.local_delta is None:
            return

        self.cumulative_offset_x += (
            e.local_delta.x / self.window_container_size[0]
            if self.window_container_size
            else 500
        )
        self.cumulative_offset_y += (
            e.local_delta.y / self.window_container_size[1]
            if self.window_container_size
            else 600
        )

        self.canvas_top_left_pos = self.mouse_pos_convert(
            (0, 0), convert_type="canvas_to_container"
        )
        print(f"当前画布左上角在容器中的位置:{self.canvas_top_left_pos}")

        self.canvas_container.offset = ft.Offset(
            self.cumulative_offset_x, self.cumulative_offset_y
        )
        print(f"当前偏移量:{self.cumulative_offset_x, self.cumulative_offset_y}")
        self.canvas_container.update()

    def mouse_hover_event(self, e: ft.HoverEvent[ft.GestureDetector]) -> None:
        """Handle mouse hover, compute canvas coordinates"""
        if e.local_delta is None:
            return

        mouse_container_x = e.local_position.x
        mouse_container_y = e.local_position.y

        self.mouse_pos_container = (mouse_container_x, mouse_container_y)
        self.mouse_pos_canvas = self.mouse_pos_convert(
            self.mouse_pos_container, convert_type="container_to_canvas"
        )

    def mouse_pos_convert(self, pos, convert_type="container_to_canvas"):
        x, y = pos
        canvas_width, canvas_height = (
            self.window_container_size if self.window_container_size else (500, 600)
        )

        scaled_width = float(canvas_width) * self.current_scale
        scaled_height = float(canvas_height) * self.current_scale

        actual_left = (
            (self.cumulative_offset_x * canvas_width)
            + self.scale_offset_x
            + (canvas_width - scaled_width) / 2
        )
        actual_top = (
            (self.cumulative_offset_y * canvas_height)
            + self.scale_offset_y
            + (canvas_height - scaled_height) / 2
        )

        if convert_type == "container_to_canvas":
            new_x = (x - actual_left) / self.current_scale
            new_y = (y - actual_top) / self.current_scale
        elif convert_type == "canvas_to_container":
            new_x = x * self.current_scale + actual_left
            new_y = y * self.current_scale + actual_top

        converted_pos = (new_x, new_y)
        return converted_pos

    def expand_canvas(self):
        canvas_width, canvas_height = (
            self.window_container_size if self.window_container_size else (500, 600)
        )
        self.canvas_container.width = canvas_width + 200
        self.canvas_container.height = canvas_height + 200
        print(f"扩张前{canvas_width, canvas_height}")
        print(f"扩张后{self.canvas_container.width, self.canvas_container.height}")
        self.window_container_size=(self.canvas_container.width, self.canvas_container.height)
        self.canvas_container.update()


if __name__ == "__main__":

    def main(page: ft.Page):
        canvas = BaseCanvas()
        page.add(
            ft.Button("+chicun", on_click=lambda e: canvas.expand_canvas()),
            ft.Container(expand=True, content=canvas),
        )

    ft.run(main)

To reproduce

step:

  1. Run the displayed code.
  2. Click the "+chicun" button (size expansion function).
    (A screen recording of the reproduction is attached)

Expected behavior

No response

Screenshots / Videos

Captures

Operating System

Windows

Operating system details

11

Flet version

0.84

Regression

Yes, it used to work in a previous Flet version (please specify the version in additional details)

Suggestions

No response

Logs

Logs
[Paste your logs here]

Additional details

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions