ScrollView上未绘制Kivy布局背景

时间:2017-05-14 17:34:32

标签: python kivy

请考虑以下代码:

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 init。所以我想这个问题并不是真正知道给TestView提供什么样的高度,但是奇怪的是能够将Label放在大致正确的位置,但不能绘制背景,或者将它从屏幕上画出来。无论如何,对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))

我不会撒谎,我觉得这很难看。看起来布局应该能够在我添加一个小部件时能够计算出它的新高度,而不必像这样用勺子喂它。无论如何,它似乎工作。我会打开这个问题,万一有人有这样做的可怕方法。

2 个答案:

答案 0 :(得分:0)

通过注释掉您提到的行(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.load_string(
"""
<TestView>:
    height: self.minimum_height
    canvas.before:
        Color:
            rgba: 0, 1, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size
""")

答案 1 :(得分:0)

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

我认为我们不应该从'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()