【问题标题】:Strange scroll behaviour in KivyKivy 中奇怪的滚动行为
【发布时间】:2016-05-27 19:17:27
【问题描述】:

我有一个ScrollView 和一个Bubble,它们部分重叠并包含一个GridLayout
问题:

  • 如何隐藏小部件而不移除它?

我已经阅读了关于该主题的 question 的答案,建议要么结合 disabledopacity 属性,这是我最终使用的,要么暂时将小部件移出屏幕.使用第一种方法隐藏Bubble,我发现即使它被禁用,它也会阻止它后面的视图滚动,即使文档声明了这个属性

指示此小部件是否可以与输入交互

所以我认为它不应该阻止滚动。有趣的是,当它没有被隐藏时(disabled=False),滚动直接穿过它,这更令人困惑

我之前也有 Bubble 包含一个 ScrollView,而它又持有 GridLayout。以下问题不再是问题,但仍然是一个有趣的行为:

  • 为什么Bubble 向上滚动传递,但向下滚动传递不传递?

要理解我的意思,运行代码,将鼠标悬停在Bubble 上,然后尝试使用鼠标滚轮向不同方向滚动。那是考虑到ScrollView 中的GridLayout 不包含任何内容,即使它不影响行为

以下是这两个问题的代码以及一些获取所需行为的说明:

from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.bubble import Bubble
from kivy.properties import ListProperty

Builder.load_string('''
<SmileBubble>:
    size_hint: None, None
    pos: 220, 90
    size: 175, 250

    #ScrollView:
        #GridLayout:
            #rows: 8        # To see the second question's example, uncomment this section
                            # and comment out the one below
    GridLayout:
        rows: 8

<MessageView>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<Message>:
    BoxLayout:
        pos: root.pos
        height: self.height
        TextInput:
            pos: root.pos
            size: root.size
            id: msg

''')

class Message(Widget):
    bg_color = ListProperty([0.99, 0.99, 0.99, 1])

class SmileBubble(Bubble):
    def hide(self):
        self.disabled = True

    def show(self):
        self.disabled = False

class MessageView(ScrollView):
    pass

class TestApp(App):
    def msg_in(self, text):
        msg = Message()
        msg.ids['msg'].text = text
        msg.size_hint = [None, None]
        msg.width = 160
        self.msg_layout.add_widget(msg)

    def build(self):
        self.scr = Screen()
        self.sv1_main = MessageView()
        self.msg_layout = GridLayout(cols = 1,
                                     size_hint_y = None)
        self.msg_layout.bind(minimum_height = self.msg_layout.setter('height'))

        self.smile_bbl = SmileBubble()
        for i in range(10):
            self.msg_in("test")

        self.smile_bbl.hide()      # To hide/show the Bubble, comment out this line. For the second question, comment out this line

        self.scr.add_widget(self.sv1_main)
        self.sv1_main.add_widget(self.msg_layout)
        self.scr.add_widget(self.smile_bbl)

        return self.scr

TestApp().run()

如果重要的话,我正在使用 Kivy v1.9.2-dev0

【问题讨论】:

    标签: python python-3.x kivy


    【解决方案1】:

    如果您不想要 opacity&disabled 技巧(不错的一个)或类似 y = 5000 的东西,在例如BoxLayout - 显然使用它会导致你的ScrollView 被拉伸,我看到“两个”简单的选项虽然不是真的没有删除 - 但保留!

    第一个是获取所有画布指令(如果您可以访问它们),将它们复制到某处并canvas.clear,但如果出现问题,祝您好运。

    第二个基本上是第一个,但是在三个命令中,你不能搞砸,除非你忘记了小部件去了哪里(呵呵):

    • 复制小部件
    • 从不可见的地方移除小部件
    • 添加以后可以访问的位置

     

    from kivy.lang import Builder
    from kivy.base import runTouchApp
    from kivy.uix.boxlayout import BoxLayout
    Builder.load_string('''
    <Test>:
        orientation: 'vertical'
        Button:
            on_release: root.move_me()
        BoxLayout:
            id: box
            Button:
                text: 'Hi %s!' % self.parent
        Button:
            id: box2
    ''')
    class Test(BoxLayout):
        def move_me(self):
            if self.ids.box.children:
                button = self.ids.box.children[0]
                self.ids.box.remove_widget(button)
                self.ids.box2.add_widget(button)
            else:
                button = self.ids.box2.children[0]
                self.ids.box2.remove_widget(button)
                self.ids.box.add_widget(button)
    runTouchApp(Test())
    

    您可以看到该小部件仍然存在于变量中,您可以通过button.&lt;something&gt; 访问其属性,正如您在text 中看到的那样。

    现在,如果您在例如BoxlayoutGridLayout 以错误的方式:父级中的小部件 pos 得到更新,并且与列表中的 [1, 2, 3].remove(2) 完全相同,最终列表将是 [1, 3],这在 BoxLayout 中意味着将大小分成两半而不是三分之二。

    如何解决这个问题?简单地说,再次使用上面的技巧,但现在保存更多小部件 - 您要隐藏的小部件 + 稍后

    添加到父级的每个小部件
    my_widgets = <parent>.children[<widget you want to hide>:len(<parent>.children)-1]
    

    这将为您提供一个对象列表,即再次按原样保留所有内容,您只会“弹出”您不想看到的小部件。最后用相同的size(甚至可能是pos,但这是自动计算的,所以......)你想隐藏的小部件创建一个占位符(例如Widget或任何透明的):

    for child in my_widgets:
        <parent>.add_widget(child)
    

    这种方法(或者更确切地说是我的解释)可能看起来很困难,但比将小部件扔出父母的边界框或尝试disabled=True 更简单,这对你来说导致无法滚动(否则我肯定会去的)。将Bubble 替换为Widget,您就可以滚动了。 Ofc 它将被放置到 [0, 0],但这不是一个论点,就像您使用 Color&Rectangle 使其可见一样,使用放置在 Widget 上的光标滚动(至少对我而言 @ 987654344@ 与您的代码)。

    最后:制作一个函数

    from kivy.lang import Builder
    from kivy.base import runTouchApp
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.widget import Widget
    Builder.load_string('''
    <Test>:
        orientation: 'vertical'
        Button:
            text: 'hide'
            on_release: root.hide(box)
        BoxLayout:
            id: box
            Button:
                text: 'Hi %s!' % self.parent
        Button
            text: 'Retrieve'
            on_release: root.hide(root.placeholder, root.saved)
    ''')
    class Test(BoxLayout):
        def hide(self, what, retrieve=None):
            # you'll need that object accessible and ".parent" will disappear
            parent = what.parent
            children = what.parent.children[:]
    
            # check for position in children list
            place = children.index(what)
    
            # save the widget you want to hide
            self.saved = children[place]
    
            # save children from the latest added to the removed one
            saved_children = children[0:place+1]
    
            # sizes are optional here
            self.placeholder = Widget(size_hint=[None, None],
                                 size=saved_children[0].size)
            for child in saved_children:
                parent.remove_widget(child)  # here you still can use what.parent
    
            # here the ".parent" is not available - the reason for "parent" var.
            # add Widget instead of whatever you will "hide"
            # pass to "retrieve" the saved widget if you want it back
            parent.add_widget(self.placeholder if not retrieve else retrieve)
    
            # add widgets in order they were originally added to the parent
            for child in list(reversed(saved_children[:place])):
                parent.add_widget(child)
            # cleanup mess ^^
            del children, saved_children
    runTouchApp(Test())
    

    【讨论】:

      猜你喜欢
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多