kivy / kivy

Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS
https://kivy.org
MIT License
17k stars 3.04k forks source link

An unbound event before ScrollView is triggered #8671

Open waseemcn opened 1 month ago

waseemcn commented 1 month ago

Software Versions

Describe the bug A clear and concise description of what the bug is. BUG in ScrollView, ScrollView added before will repeatedly bind on_scroll_stop event, ScrollView added after the event does not trigger events, no event is bound before the addition

Expected behavior A clear and concise description of what you expected to happen. Events that are not bound in the previous ScrollView are not fired

To Reproduce A short, runnable example that reproduces the issue with latest kivy master.

from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.uix.scrollview import ScrollView

class IndexScreen(Screen):
    def __init__(self, **kwargs):
        super(IndexScreen, self).__init__(**kwargs)
        self.init_layout()

    def init_layout(self):
        """初始化屏幕布局"""
        try:
            self.layout = BoxLayout(orientation='vertical')

            # 假设header、middle、button方法分别用于添加头部、中间内容和按钮区域的组件
            self.add_header()
            self.add_middle()
            self.add_button()

            self.add_widget(self.layout)
        except Exception as e:
            # 在实际应用中应该有更具体的异常处理
            print(f"布局初始化出错: {e}")

    def add_header(self):
        """添加头部组件"""
        self.topBoxLayout = BoxLayout(orientation='horizontal', size_hint_y=0.3, padding=[0, 0, 0, 2])
        header_scrollview = ScrollView(size_hint=(1, 1))
        header_inner_layout = BoxLayout(orientation='horizontal', size_hint=(None, 1), spacing=10, padding=[0, 0, 0, 3])
        header_inner_layout.bind(minimum_width=header_inner_layout.setter('width'))

        # 方案一:
        for i in range(10):
            topButton = Button(text=f'topButton{i}', size_hint_x=None, width=200,
                               halign='center', valign='center')
            header_inner_layout.add_widget(topButton)

        header_scrollview.add_widget(header_inner_layout)
        self.topBoxLayout.add_widget(header_scrollview)
        self.layout.add_widget(self.topBoxLayout)

    def add_middle(self):
        self.left_scroll_box = BoxLayout(orientation='vertical', size_hint_x=0.3, padding=[0, 0, 1, 0])

        left_scrollview = ScrollView(size_hint=(1, 1))
        left_inner_layout = BoxLayout(orientation='vertical', size_hint=(1, None), spacing=5, padding=[0, 0, 3, 0])
        left_inner_layout.bind(minimum_height=left_inner_layout.setter('height'))

        for i in range(13):
            middleButton = Button(text=f'leftButton{i}', size_hint_y=None, background_color=(0.8, 0.95, 0.75, 0.8))
            left_inner_layout.add_widget(middleButton)

        left_scrollview.add_widget(left_inner_layout)

        def on_scroll_stop(instance, value):
            scroll_y = instance.scroll_y

            content_height = instance.viewport_size[1]

            scroll_view_height = instance.height

            max_scroll_range = content_height - scroll_view_height

            if max_scroll_range > 0:
                real_value = scroll_y * max_scroll_range
                print("Real value:", real_value)

                if real_value <= 0:
                    # 在这里添加你要执行的操作,例如:
                    print("Scroll to top or nearly top, consider adding labels or similar actions.")
            else:
                print("Max scroll range is negative or zero, real value calculation skipped.")

        left_scrollview.bind(
            on_scroll_stop=on_scroll_stop)
        self.left_scroll_box.add_widget(left_scrollview)
        self.layout.add_widget(self.left_scroll_box)

    def add_button(self):
        """添加按钮区域"""
        self.bottomBoxLayout = BoxLayout(orientation='vertical', size_hint_y=0.2, padding=[0, 1, 0, 2])

        bottom_scrollview = ScrollView(size_hint=(1, 1))
        bottom_inner_layout = BoxLayout(orientation='horizontal', size_hint=(None, 1), spacing=10, padding=[0, 3, 0, 3])
        bottom_inner_layout.bind(minimum_width=bottom_inner_layout.setter('width'))

        for i in range(3):
            buttomButton = Button(text=f'bottomButton{i}', size_hint_x=None, width=200)
            bottom_inner_layout.add_widget(buttomButton)

        bottom_scrollview.add_widget(bottom_inner_layout)
        self.bottomBoxLayout.add_widget(bottom_scrollview)

        self.layout.add_widget(self.bottomBoxLayout)

class ScrollApp(App):
    def build(self):
        Window.clearcolor = (1, 1, 1, 1)
        return IndexScreen()

if __name__ == "__main__":
    ScrollApp().run()

LOG INFO

[INFO   ] [Logger      ] Record log in C:\Users\123\.kivy\logs\kivy_24-04-04_58.txt

[INFO   ] [deps        ] Successfully imported "kivy_deps.angle" 0.4.0

[INFO   ] [deps        ] Successfully imported "kivy_deps.glew" 0.3.1

[INFO   ] [deps        ] Successfully imported "kivy_deps.sdl2" 0.7.0

[INFO   ] [Kivy        ] v2.3.0

[INFO   ] [Kivy        ] Installed at "C:\Users\123\PycharmProjects\pythonProject\.venv\Lib\site-packages\kivy\__init__.py"

[INFO   ] [Python      ] v3.12.2 (tags/v3.12.2:6abddd9, Feb  6 2024, 21:26:36) [MSC v.1937 64 bit (AMD64)]

[INFO   ] [Python      ] Interpreter at "C:\Users\123\PycharmProjects\pythonProject\.venv\Scripts\python.exe"

[INFO   ] [Logger      ] Purge log fired. Processing...

[INFO   ] [Logger      ] Purge finished!

[INFO   ] [Factory     ] 195 symbols loaded

[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)

[INFO   ] [Window      ] Provider: sdl2

[INFO   ] [GL          ] Using the "OpenGL" graphics system

[INFO   ] [GL          ] GLEW initialization succeeded

[INFO   ] [GL          ] Backend used <glew>

[INFO   ] [GL          ] OpenGL version <b'4.6.0 Compatibility Profile Context 24.1.1.231114'>

[INFO   ] [GL          ] OpenGL vendor <b'ATI Technologies Inc.'>

[INFO   ] [GL          ] OpenGL renderer <b'AMD Radeon RX 580 2048SP'>

[INFO   ] [GL          ] OpenGL parsed version: 4, 6

[INFO   ] [GL          ] Shading version <b'4.60'>

[INFO   ] [GL          ] Texture max size <16384>

[INFO   ] [GL          ] Texture max units <32>

[INFO   ] [Window      ] auto add sdl2 input provider

[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked

[INFO   ] [Text        ] Provider: sdl2

[INFO   ] [Base        ] Start application main loop

[INFO   ] [GL          ] NPOT texture support is available