【问题标题】:Kivy Layout Background Not Painted on ScrollViewKivy 布局背景未在 ScrollView 上绘制
【发布时间】:2017-05-14 17:34:32
【问题描述】:

请考虑以下代码:

Builder.load_string(
"""
<TestView>:
    canvas.before:
        Color:
            rgba: 0, 1, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size
""")

class TestView(GridLayout):

    def __init__(self, **kwargs):
        super(TestView, self).__init__(**kwargs)

        self.cols = 1
        self.spacing = 20

        self.add_widget(Label(text="I'm on the TestView", height=80 ))

class TestScreen(Screen):

    def __init__(self, **kwargs):
        super(TestScreen, self).__init__(**kwargs)

        self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
        self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))

        self.content_grid_layout.add_widget(Label(text="A"))
        self.content_grid_layout.add_widget(Label(text="B"))

        self.content_grid_layout.add_widget(TestView())

        self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
        self.scroll_view.add_widget(self.content_grid_layout)

        self.add_widget(self.scroll_view)

sm = ScreenManager()
sm.add_widget(TestScreen(name="test_screen"))

class TestApp(App):

    def build(self):
        return sm

if __name__ == '__main__':
    TestApp().run()

(我省略了导入以节省空间)。所以我在这里的具体问题是我希望 TestView 的背景被涂成绿色,但它不在这里。如果您只是将 TestView 直接添加到 Screen 的布局中,即更改 TestScreen 的 init 以执行以下操作:

self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
self.content_grid_layout.add_widget(TestView())
self.add_widget(self.content_grid_layout)

但是,我需要将我的内容放在此处的 ScrollView 上。所以我还注意到,如果我只是注释掉这一行:

self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))

从原始代码来看,一切似乎都按预期工作,并且 TestView 的背景被涂成绿色。但是,如果我这样做,结果 ScrollView 并没有达到我的预期(它不会滚动)。

如果我给 TestView 一个明确的高度,那么一切都会按预期工作,例如添加:

self.size_hint_y = None
self.height = 40

到 TestView 初始化。所以我猜问题是不是真的知道给TestView的高度是多少,但是奇怪的是能够把标签放在大约正确的位置,但不能画背景,或者把它画在屏幕上或其他东西上。无论如何,对 TestView 进行硬编码对我不起作用,因为 IRL 它会包含一些高度不同的动态内容。

我觉得在一个理智的世界里,布局的高度就是其内容的高度,除非另有说明,但也许这就是我。我想如果我可以根据其内容调整 TestView 的大小,那么我会得到我期望的行为吗?


编辑:

好的,我想我可以按照这里描述的策略让它做我想做的事:https://groups.google.com/d/msg/kivy-users/xRl2l8-1qLs/zzpk-QG4C0MJ (特别是那里描述的 CustomGridLayout,我猜这就像我的 TestView)。

所以基本的想法似乎是我们最初将自定义 GridLayout (TestView) 设置为零高度,然后为我们添加的每个子项手动更新其高度,如下所示:

class TestView(GridLayout):

    def __init__(self, **kwargs):
        super(TestView, self).__init__(**kwargs)

        self.cols = 1
        self.spacing = 20, 20

        self.size_hint_y = None
        self.height = 0

        self.height = self.height + 30 + self.spacing[1]
        self.add_widget(Label(text="I'm on the TestView 1", size_hint_y=None, height=30))
        self.height = self.height + 30 + self.spacing[1]
        self.add_widget(Label(text="I'm on the TestView 2", size_hint_y=None, height=30))
        self.height = self.height + 30 + self.spacing[1]
        self.add_widget(Label(text="I'm on the TestView 3", size_hint_y=None, height=30))

我不会撒谎,我认为这很丑陋。当我向它添加一个小部件时,布局似乎应该能够计算出它的新高度,而不必像这样用勺子喂它。无论如何,它似乎工作。如果有人这样做的方式并不可怕,我将保留这个问题。

【问题讨论】:

    标签: python kivy


    【解决方案1】:

    通过注释掉您提到的行 (self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))) 并将 height: self.minimum_height 添加到 Builder 字符串中,我能够让您的 TestView 变为绿色。 TestScreen 也是可滚动的。

    考虑将 TestScreen 初始化器更改为:

    def __init__(self, **kwargs):
        super(TestScreen, self).__init__(**kwargs)
    
        self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
        # self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
    
        self.content_grid_layout.add_widget(Label(text="A"))
        self.content_grid_layout.add_widget(Label(text="B"))
    
        self.content_grid_layout.add_widget(TestView())
    
        self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
        self.scroll_view.add_widget(self.content_grid_layout)
    
        self.add_widget(self.scroll_view)
    

    还有 Builder 字符串到这个:

    Builder.load_string(
    """
    <TestView>:
        height: self.minimum_height
        canvas.before:
            Color:
                rgba: 0, 1, 0, 1
            Rectangle:
                pos: self.pos
                size: self.size
    """)
    

    【讨论】:

    • 谢谢,但添加 height: self.minimum_height 似乎没有任何区别。使用 self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height')) 行我没有得到绿色背景,没有它我会得到,无论我们是否有高度:self.minimum_height,并且没有self.content_grid_layout.bind(minimum_height 行滚动不起作用。我正在按照kivy.org/docs/api-kivy.uix.scrollview.html 的示例添加一堆按钮来测试滚动
    【解决方案2】:

    好的,我想我已经破解了 - Edvardas 将 TestView 的最小高度与其高度绑定的概念是正确的(我想这对我来说应该是显而易见的,因为这也是我们为 'content_grid_layout' 所做的事情)。但是,它不起作用,因为我们还需要 'size_hint_y: None'。

    我认为我们不应该从“content_grid_layout”中取出高度/最小高度绑定,因为这会停止滚动工作。

    这样做可以按预期工作,我们不必手动设置 TestView 的高度。这是一个完整的工作示例,可以避免任何混淆 - 我已将 TestView 从 GridLayout 更改为 BoxLayout,但它也可以用作 GridLayout:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.uix.label import Label
    from kivy.uix.button import Button
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.scrollview import ScrollView
    
    Builder.load_string(
    """
    <TestView>:
        size_hint_y: None            # <----------------- MAKE SURE YOU DO THIS!
        height: self.minimum_height  # <----------------- AND THIS!
        canvas.before:
            Color:
                rgba: 0, 0.8, 0.06, 0.5
            RoundedRectangle:
                pos: self.pos
                size: self.size
                radius: [16,16,16,16]
    """)
    
    class TestView(BoxLayout):
    
        def __init__(self, **kwargs):
            super(TestView, self).__init__(**kwargs)
    
            self.orientation = 'vertical'
            self.padding = [20, 20, 20, 20]
            self.spacing = 20
    
            self.add_widget(Label(text="I'm on the TestView 1"))
            self.add_widget(Label(text="I'm on the TestView 2"))
            self.add_widget(Label(text="I'm on the TestView 3"))
    
    class TestScreen(Screen):
    
        def __init__(self, **kwargs):
            super(TestScreen, self).__init__(**kwargs)
    
            self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
            self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
    
            # Give us something to scroll:
            for i in range(20):
                btn = Button(text=str(i), size_hint_y=None, height=40)
                self.content_grid_layout.add_widget(btn)
                if i == 5:
                    self.content_grid_layout.add_widget(TestView())
    
            self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
            self.scroll_view.add_widget(self.content_grid_layout)
    
            self.add_widget(self.scroll_view)
    
    sm = ScreenManager()
    sm.add_widget(TestScreen(name="test_screen"))
    
    class TestApp(App):
    
        def build(self):
            return sm
    
    if __name__ == '__main__':
        TestApp().run()
    

    【讨论】:

      猜你喜欢
      • 2019-11-14
      • 1970-01-01
      • 1970-01-01
      • 2019-12-20
      • 2012-10-26
      • 1970-01-01
      • 2013-01-07
      • 2023-03-22
      • 1970-01-01
      相关资源
      最近更新 更多