kivy 中的属性绑定和样式问题

Posted

技术标签:

【中文标题】kivy 中的属性绑定和样式问题【英文标题】:Problems with property binding and styling in kivy 【发布时间】:2019-11-24 18:48:51 【问题描述】:

目标 我有一个带有属性c_description 的小部件类。我需要能够创建标签(或其他类型的小部件)并将其添加到这个继承 c_description 作为文本的小部件。并且对c_description 的更改会传播到标签的文本。我需要能够使用函数运行此标签的创建/添加。 我基本上需要完全按照this other question 的要求去做。

我做了什么,遇到了什么问题 我格式化了这个标签类StretchingLabelstretch 以适应它的内容。我有一个script in kivy,它显示了我希望它如何工作。 最终结果应该是这样的。

这是other script,在将其text 属性绑定到c_description 失败后,我在其中动态创建并添加了一个新小部件。 如果我运行它,我会得到如下所示的结果。

标签的文本是“”而不是c_description 的内容,所以这是一个问题。 但是,如果我删除属性绑定语句并将c_label = StretchingLabel() 更改为c_label = StretchingLabel(pos=self.pos, width=self.width, text=self.c_description),我们至少应该能够看到成功的属性绑定是什么样的。 当我这样做时,结果看起来像这样。 这不是我想要的。我希望它看起来像第一张照片。

我的代码

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.clock import Clock
    from kivy.uix.widget import Widget
    from kivy.uix.label import Label
    from kivy.uix.boxlayout import BoxLayout
    from kivy.properties import StringProperty
    from kivy.uix.textinput import TextInput

    Builder.load_string('''
    <StretchingLabel>:
        size_hint_y: None
        text_size: self.width, None
        height: self.texture_size[1]
        group: 'test'
        canvas.before:
            Color:
                rgba: .7, .7, .7, 1
            Rectangle:
                pos: self.pos
                size: self.size

    <MyLabelFrame>:
        id: xLabel

    <ContainerBox>:
        orientation: 'horizontal'
        Button:
            text: 'h1'
            group: 'test'
        BoxLayout:
            orientation: 'vertical'
            size: root.size
            pos: root.pos
            Label:
                text: 'Description'
                size_hint_y: None
                height: 30
                bold: True
            MyLabelFrame:
            Label:
    ''')

    class StretchingLabel(Label):
        def __init__(self, **kwargs):
            super(StretchingLabel, self).__init__(**kwargs)
            #This is for debugging
            Clock.schedule_once(lambda dt: print("StretchingLabel.init(): ", self.text), timeout=0.01)
        def on_double_click(self, instance, p_ignoreme):
            #This is also for debugging
            print("StretchingLabel.on_double_click():", self.text)

    class MyLabelFrame(Widget):
        c_description = StringProperty(
            'Lorem ipsum dolor sit amet, consectetur adipiscing elit. \n\nProin vitae turpis ornare urna elementum pharetra non et tortor. Curabitur semper mattis viverra. \nPellentesque et lobortis purus, eu ultricies est. Nulla varius ac dolor quis mattis. Pellentesque vel accumsan tellus. Donec a nunc urna. Nulla convallis dignissim leo, tempor sagittis orci sollicitudin aliquet. Duis efficitur ex vel auctor ultricies. Etiam feugiat hendrerit mauris suscipit gravida. Quisque lobortis vitae ligula eget tristique. Nullam a nulla id enim finibus elementum eu sit amet elit.')

        def __init__(self, **kwargs):
            super(MyLabelFrame, self).__init__(**kwargs)
            Clock.schedule_once(lambda dt: self.makeLabel(), timeout=0.01)

        def makeLabel(self):
            c_label = StretchingLabel()
            #HERE! This vvv does not seem to work for some reason.
            self.bind(pos=c_label.setter('pos'), width=c_label.setter('width'), c_description=c_label.setter('text'))
            #This vvv didn't work either.
            #c_label.bind(pos=self.setter('pos'), width=self.setter('width'), text=self.setter('c_description'))
            self.add_widget(c_label)

    class ContainerBox(BoxLayout):
        def __init__(self, **kwargs):
            super(ContainerBox, self).__init__(**kwargs)

    class Nested2App(App):
        def build(self):
            return ContainerBox()

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

我的问题

    为什么self.bind(c_description=c_label.setter('text')) 不起作用?正在创建标签,但没有从 c_description 获取文本。我做错了什么? 为什么创建的标签格式错误?我在 kv 代码中传递了与 makeLabel() 中相同的属性。我需要能够使用makeLabel() 函数正确执行此操作。

注意

我看到another question here 遇到了与我完全相同的问题(没有格式问题),但由于某种原因,答案对我不起作用。

【问题讨论】:

【参考方案1】:

问题 1

为什么 self.bind(c_description=c_label.setter('text')) 不起作用?这 正在创建标签,但没有从中获取文本 c_description。我做错了什么?

回答

你得到了正确的代码,

self.bind(pos=c_label.setter('pos'), width=c_label.setter('width'), c_description=c_label.setter('text'))

绑定过程不会立即更新texttext 只会在 c_description 更改时更改。

示例

以下示例说明了以下内容:

    最初显示一个空标签 在更改后显示c_descripton,但在小部件的默认pos=[0,0]width=100 最大化窗口,c_description 显示在正确的位置,因为 poswidth 已更改。

注意:-kv 文件

color: 0, 0, 0, 1 # black colour text 添加到类规则 &lt;StretchingLabel&gt;: 中,因为文本不可见。这是因为背景颜色是白色,Label 的文本默认颜色也是白色。

main.py

from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty

Builder.load_string('''
<StretchingLabel>:
    color: 0, 0, 0, 1   # black color text    

    size_hint_y: None
    text_size: self.width, None
    height: self.texture_size[1]   
    group: 'test'

    canvas.before:
        Color:
            rgba: .7, .7, .7, 1
        Rectangle:
            pos: self.pos
            size: self.size

<MyLabelFrame>:
    id: xLabel

<ContainerBox>:
    orientation: 'horizontal'
    Button:
        text: 'h1'
        group: 'test'

    BoxLayout:
        orientation: 'vertical'
        size: root.size
        pos: root.pos

        Label:
            text: 'Description'
            size_hint_y: None
            height: 30
            bold: True

        MyLabelFrame:

        Label:
''')


class StretchingLabel(Label):

    def __init__(self, **kwargs):
        super(StretchingLabel, self).__init__(**kwargs)
        # This is for debugging
        Clock.schedule_once(lambda dt: print("StretchingLabel.init(): ", self.text), timeout=0.01)

    def on_double_click(self, instance, p_ignoreme):
        # This is also for debugging
        print("StretchingLabel.on_double_click():", self.text)


class MyLabelFrame(Widget):
    c_description = StringProperty(
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. \n\nProin vitae turpis ornare urna elementum pharetra non et tortor. Curabitur semper mattis viverra. \nPellentesque et lobortis purus, eu ultricies est. Nulla varius ac dolor quis mattis. Pellentesque vel accumsan tellus. Donec a nunc urna. Nulla convallis dignissim leo, tempor sagittis orci sollicitudin aliquet. Duis efficitur ex vel auctor ultricies. Etiam feugiat hendrerit mauris suscipit gravida. Quisque lobortis vitae ligula eget tristique. Nullam a nulla id enim finibus elementum eu sit amet elit.')

    def __init__(self, **kwargs):
        super(MyLabelFrame, self).__init__(**kwargs)
        Clock.schedule_once(lambda dt: self.makeLabel(), timeout=0.8)

    def makeLabel(self):
        c_label = StretchingLabel()
        self.bind(pos=c_label.setter('pos'), width=c_label.setter('width'), c_description=c_label.setter('text'))
        self.add_widget(c_label)

        Clock.schedule_once(lambda dt: self.chg_text(), 1)

    def chg_text(self):
        self.c_description = 'Updated: ...' + self.c_description


class ContainerBox(BoxLayout):
    def __init__(self, **kwargs):
        super(ContainerBox, self).__init__(**kwargs)


class Nested2App(App):
    def build(self):
        return ContainerBox()


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

输出

应用启动

c_description已更新

最大化和最小化窗口

问题 2

为什么创建的标签格式错误?我通过了同样的 我在 makeLabel() 中所做的 kv 代码中的属性。我需要成为 能够使用 makeLabel() 函数正确地做到这一点。

根本原因

问题是由于 Kivy 的样式未完成。

解决方案

timeout 值增加到至少 0.8 秒。该值会有所不同,即取决于您的计算机速度。

片段

    def __init__(self, **kwargs):
        super(MyLabelFrame, self).__init__(**kwargs)
        Clock.schedule_once(lambda dt: self.makeLabel(), timeout=0.8)

    def makeLabel(self):
        c_label = StretchingLabel(pos=self.pos, width=self.width, text=self.c_description)
        self.add_widget(c_label)

输出

【讨论】:

这绝对解决了问题 #2。但我至少花了 1.4 秒(有时这还不够)。这个时间是否会根据一个人的计算机速度而有所不同? 没错,它因人而异。这取决于计算机的速度。 问题1的详细解释请参考更新后的帖子。 好的,对于所有未来的人,我将转述你的解释,看看我是否理解。 self.bind(c_description=c_label.setter('text')) 确实将c_description 正确绑定到标签的text。但是,此绑定仅在 c_description 更改时更新标签的文本(从而触发属性事件)。所以标签的文本最初是“”,并且只有在c_description 更改时才会更改为c_description 的值。对吗?

以上是关于kivy 中的属性绑定和样式问题的主要内容,如果未能解决你的问题,请参考以下文章

将 Kivy 列表项中的 on_release 绑定到函数

Kivy Scrollview:AttributeError:“NoneType”对象没有属性“绑定”

使用计算属性和 VueX 的样式绑定

将路径中的填充属性绑定到样式中 ContentControl 中的 Foreground 属性

将函数绑定到kivy中的按钮

Kivy,Python3.5 - 将用户文本输入绑定到类中的方法