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

Kivy中如何结合_on_keyboard_down事件实现TreeView上下滚动

Solution for Auto-Scrolling TreeView with Arrow Keys

Alright, let's tackle this problem. You've got a TreeView in a Popup with scroll functionality, and you want the scroll view to automatically follow the selected node when using the up/down arrows. Here's how to make that work with your existing Kivy setup:

Modified Code Snippet

from kivy.uix.popup import Popup
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.properties import ObjectProperty, NumericProperty

class TreeviewGroup(Popup):
    treeview = ObjectProperty(None)
    tv = ObjectProperty(None)
    h = NumericProperty(0)
    popup = ObjectProperty()
    scroll_view = ObjectProperty(None)  # Add reference to ScrollView

    def __init__(self, **kwargs):
        super(TreeviewGroup, self).__init__(**kwargs)
        # Initialize TreeView
        self.tv = TreeView(root_options=dict(text=""), hide_root=False, indent_level=4)
        
        # Add your rows/nodes here (replace with your actual data)
        rows = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8"]
        for row in rows:
            self.tv.add_node(TreeViewLabel(text=row))
        
        # Set up ScrollView and add TreeView to it
        self.scroll_view = ScrollView(size_hint=(1, 1))
        self.tv.size_hint_y = None  # Disable automatic height sizing
        self.update_tree_height()  # Calculate initial TreeView height
        self.scroll_view.add_widget(self.tv)
        self.add_widget(self.scroll_view)
        
        # Bind keyboard events
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        
        # Update TreeView height when nodes are added/removed
        self.tv.bind(on_node_added=self.update_tree_height, on_node_removed=self.update_tree_height)

    def _keyboard_closed(self):
        """Clean up keyboard binding when closed"""
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        """Handle up/down arrow key presses to select nodes and scroll"""
        current_node = self.tv.selected_node

        # Handle Up Arrow
        if keycode[1] == 'up':
            if current_node and current_node.parent_node:
                prev_node = self.tv.get_previous_node(current_node)
                if prev_node:
                    self.tv.selected_node = prev_node
                    self.scroll_to_selected()
                return True  # Consume the key event
        
        # Handle Down Arrow
        elif keycode[1] == 'down':
            if current_node:
                next_node = self.tv.get_next_node(current_node)
                if next_node:
                    self.tv.selected_node = next_node
                    self.scroll_to_selected()
                return True  # Consume the key event
        
        return False  # Let other handlers process unrecognized keys

    def scroll_to_selected(self):
        """Adjust ScrollView to bring selected node into view"""
        selected_node = self.tv.selected_node
        if not selected_node or not self.scroll_view:
            return
        
        # Get dimensions of selected node and ScrollView
        node_y, node_height = selected_node.y, selected_node.height
        scroll_view_height = self.scroll_view.height
        tree_total_height = self.tv.height

        # Calculate visible bounds of the ScrollView
        scroll_offset = self.scroll_view.scroll_y * (tree_total_height - scroll_view_height)
        visible_top = scroll_offset + scroll_view_height
        visible_bottom = scroll_offset

        # Calculate node's top and bottom positions
        node_top = node_y + node_height
        node_bottom = node_y

        # Scroll up if node is below visible area
        if node_top > visible_top and tree_total_height > scroll_view_height:
            new_scroll_y = (tree_total_height - node_top) / (tree_total_height - scroll_view_height)
            self.scroll_view.scroll_y = max(0.0, new_scroll_y)
        
        # Scroll down if node is above visible area
        elif node_bottom < visible_bottom and tree_total_height > scroll_view_height:
            new_scroll_y = (tree_total_height - node_bottom - scroll_view_height) / (tree_total_height - scroll_view_height)
            self.scroll_view.scroll_y = min(1.0, new_scroll_y)

    def update_tree_height(self, *args):
        """Calculate total height of TreeView based on all nodes"""
        total_height = 0
        for node in self.tv.iterate_all_nodes():
            total_height += node.height
        self.tv.height = total_height

Key Explanations

  • TreeView Node Navigation: We use Kivy's built-in get_previous_node() and get_next_node() methods to handle selecting the correct up/down nodes. These methods automatically account for collapsed/expanded branches, which is way more reliable than manual traversal.
  • ScrollView Integration: We explicitly set tv.size_hint_y = None so the TreeView uses its actual content height instead of stretching to fill the ScrollView. The update_tree_height() method calculates the total height of all nodes and updates the TreeView's height dynamically when nodes are added or removed.
  • Auto-Scroll Logic: The scroll_to_selected() method checks if the selected node is outside the ScrollView's visible bounds. If it is, it adjusts the scroll_y property to bring the node into view. We clamp the scroll_y value between 0.0 and 1.0 to prevent invalid scroll positions.
  • Keyboard Cleanup: The _keyboard_closed() method ensures we unbind the keyboard event when the Popup is closed, preventing memory leaks.

Just replace the sample rows list with your actual node data, and this should work seamlessly with your existing setup.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:31:57