包装 Kivy 标签的文本

Posted

技术标签:

【中文标题】包装 Kivy 标签的文本【英文标题】:Wrapping the text of a Kivy Label 【发布时间】:2017-09-25 18:09:11 【问题描述】:

所以我正在使用 Kivy GUI 编写程序,我真的不想使用 .kv 文件,而是将其全部写入 python 文件。问题是在MainScreen 类中我想添加一些标签,但我不能根据窗口大小的变化使它们的文本换行。当我尝试self.size 时,我只能得到 100x100。我已经尝试了 Kivy 教程书中的所有建议,但没有任何效果。我知道我可以使用\n 使文本多行或设置标准标签大小,但这不是一个真正的解决方案。我需要标签大小和文本来跟随整个窗口的变化和要换行的文本。

我已经简化了程序,只关注这个问题。

代码如下:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.graphics import Rectangle, Color


class MainScreen(FloatLayout):
    """MAIN WINDOW CLASS"""

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

        with self.canvas.before:
            Color(0.988, 0.725, 0.074, 1, mode='rgba')
            self.rect = Rectangle(pos=self.pos, size=self.size)
        self.bind(size=self.update_rect)


        #TITLE LABEL
        self.add_widget(Label(text="A very Long Sentence that needs to be wrapped.",
                              bold = True,
                              font_size="20sp",
                              pos_hint='center_x': 0.5, 'center_y': .85,
                              size_hint=(None, None),
                              halign="center",
                              color=(0.055, 0.235, 0.541, 1)))

    def update_rect(self, *args):
        """FUNCTION TO UPDATE THE RECATANGLE OF CANVAS TO FIT THE WHOLE SCREEN OF MAINSCREEN ALWAYS"""
        self.rect.pos = self.pos
        self.rect.size = self.size

class main(App):
    """BUILDING THE APP"""
    def build(self):
        return MainScreen()

if __name__ == "__main__":
    main().run()

谢谢。

【问题讨论】:

【参考方案1】:

首先,让我告诉你,你会让你的生活变得更加艰难,kv 并不难学,它可以让 kivy 为你做更多的繁重工作。

在 kv 中,做类似的事情

Label:
    text: 'blah blah ' * 1000
    text_size: self.width, None
    size_hint: 1, None
    height: self.texture_size[1]

通常会做到这一点,因为小部件正在使用其容器的整个宽度,并且 text_size 被绑定到它的宽度,每次宽度变化时都会重新计算纹理,并且小部件的高度会自动适应纹理的高度。

现在,在没有 kv 的情况下做同样的事情。

  label = Label(text='blah blah '* 1000, size_hint=(1, None))
  label.bind(
      width=lambda *x: label.setter('text_size')(label, (label.width, None),
      texture_size=lambda *x: label.setter('height')(label, label.texture_size[1]))

也就是说,您需要明确告诉 kivy 它必须对标签的各种属性的变化做出反应,并重新计算其他属性的值,而使用 kv,它会自动检测某些值依赖于其他值,并且会知道它需要自己绑定它们。

setter 是一个实用函数,它允许您避免在简单的情况下定义 setter 函数,如果不是这样,在这里,您必须为每个绑定定义一个函数,并执行类似“self.text_size”的操作= self.width, None",因为 lambda 不允许赋值。

kv 存在的原因之一是它允许我们自己处理代码中的表达式,并检测依赖关系,如果你全部在 python 中完成,kivy 无法检测到这种依赖关系,因为 python 本身不'不关心他们,也不会告诉kivy他们。

现在,如果您想告诉我们您为什么要避免使用 kv,请这样做,但我很确定这些原因来自对 kv 的不了解,而不是它的真正问题。

【讨论】:

感谢您抽出宝贵时间给出如此分析性的答案。但是,您很快就能判断出我对 kv 的了解。我已经使用 kv 完成了这项工作。我只想使用 python 有两个主要原因。 1) 在selfrootparent 范围之外对标签参数使用动态变化的变量。 2) 出于教育原因。最后,我想出了一个简单的解决方案,稍后我会在有更多时间的时候在这里发布。正如你所说,我让我的生活变得更加艰难,但现在我对python有了更多的了解。再次感谢您。 好吧,很抱歉假设,很遗憾看到人们试图避免 kv 因为他们不明白这一点太常见了。 以编程方式创建标签或按钮时如何操作?我从来没有使用过那些笨拙的小部件模板东西。但是当以编程方式创建一个小部件时,我得到的印象是你不能使用动态公式;没有明显的“自我”等价物,如果我使用例如btn.width 只会获取静态值,而不是按钮的动态分配的运行时宽度。 好吧,您可以将函数绑定到公式的所有属性,然后在该函数中,将公式应用于您要设置的属性。但我的偏好是为包含它的小部件类编写一个 kv 规则,然后从 kv 创建小部件的实例,两全其美。【参考方案2】:

我会使用美妙的 OOP。

你可以像这样创建类:

class WrappedLabel(Label):
    # Based on Tshirtman's answer
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(
            width=lambda *x:
            self.setter('text_size')(self, (self.width, None)),
            texture_size=lambda *x: self.setter('height')(self, self.texture_size[1]))

然后在您想要“包装行为”的代码的任何部分使用您的类。 示例:

my_label = WrappedLabel(text="A very L" + "o"*200 + "ng Sentence that needs to be wrapped.",
                        bold=True,
                        font_size="20sp")

【讨论】:

太棒了,这对我有用。同意之前的 cmets 的观点,即 .kv 文件可能是正确的答案,但在最初的几个应用程序中,您会在学习曲线之上进入学习曲线。【参考方案3】:

所以在对我的问题进行了深思熟虑并尝试了不同的方法之后,我最终得到了一个合适的“仅 python”解决方案。

首先我的回答是基于 1) 官方 Kivy 教程建议,2) 画布方法的通常操作和 3) 对变量范围的仔细处理。

因此标签设置类似于 Kivy 教程书中建议的设置。需要有一个函数来更新标签位置及其文本大小(此处为setting_function)以与标签大小绑定。此外,我将 Label 分配给一个变量,以便以后可以轻松引用它。

在所有这些更改之后,我的代码如下所示:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.graphics import Rectangle, Color


class MainScreen(FloatLayout, Label):
    """MAIN WINDOW CLASS"""

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

        with self.canvas.before:
            Color(0.988, 0.725, 0.074, 1, mode='rgba')
            self.rect = Rectangle(pos=self.pos, size=self.size)
        self.bind(size=self.update_rect)

        #TITLE LABEL
        self.titlos = Label(text="A very Long Sentence that needs to be wrapped.",
                              bold = True,
                              text_size=(None,None),
                              font_size="20sp",
                              pos_hint='center_x': 0.5, 'center_y': .85,
                              size_hint_y=None,
                              size = self.size,
                              height=self.texture_size[1],
                              halign="center",
                              valign = "middle",
                              color=(0.055, 0.235, 0.541, 1))

        self.add_widget(self.titlos)
        self.titlos.bind(size=self.setting_function)

    def setting_function(self, *args):
        """FUNCTION TO UPDATE THE LABEL TO ADJUST ITSELF ACCORDING TO SCREEN SIZE CHANGES"""
        self.titlos.pos_hint = 'center_x': 0.5, 'center_y': .85
        self.titlos.text_size=self.size

    def update_rect(self, *args):
        """FUNCTION TO UPDATE THE RECATANGLE OF CANVAS TO FIT THE WHOLE SCREEN OF MAINSCREEN ALWAYS"""
        self.rect.pos = self.pos
        self.rect.size = self.size


class main(App):
    """BUILDING THE APP"""
    def build(self):
        return MainScreen()

if __name__ == "__main__":
    main().run()

上面的代码确实符合我在问题中设置的要求。很容易将它用于您自己的项目。只需注意变量的范围并使用大量打印消息进行检查。

最后,我想只使用 Python 来解决这个问题(而不仅仅是使用 kivy 语言来处理这些问题就像小菜一碟)的原因是我想要在运行时动态更改变量,即用作标签参数。而且我只需要找出答案,因为我很固执。

谢谢。

【讨论】:

以上是关于包装 Kivy 标签的文本的主要内容,如果未能解决你的问题,请参考以下文章

kivy:更改动态生成的标签的标签文本

Kivy 2 行标签文本

将文本从文本输入传递到 Kivy 中的标签

Kivy Clipboard.copy 标签文本

Kivy:标签文本在 for 循环期间不更新

如何在 Kivy、Python 中更新标签的文本