如何在Kivy中将图像放入动态文本中

时间:2018-05-24 13:24:32

标签: python-3.x kivy

我正在努力工作,希望将图片嵌入到显示的文本中。 文本被加载一个简单的字符串,然后显示,这很容易做,但我不知道如何找到如何在所述字符串中显示图像的线索。

这个问题的答案是为这些构建一个新的布局,在这种情况下是TextWrapper。

示例代码:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Label, Button
from kivy.lang import Builder
from kivy.properties import BooleanProperty, StringProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.image import Image
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView

Builder.load_string("""
<ScreenSpecies>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            pos_hint: {"x": .45, "top": 1}
            size_hint: .1, .1
            text: "The Species"

        GridLayout:
            id: species_layout
            rows: 1
            cols: 2
            padding: dp(10)
            spacing: dp(10)
            orientation: 'horizontal'

            SpeciesView:
                id: species_list_view

            SpeciesLabel:
                id: species_text
                text_selected: "Text" if not species_list_view.text_selected else species_list_view.text_selected
                name_selected: "" if not species_list_view.name_selected else species_list_view.name_selected


<SpeciesView>:
    viewclass: 'SelectableLabel'
    text_selected: ""
    name_selected: ""

    SelectableRecycleBoxLayout:
        orientation: 'vertical'
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<SpeciesLabel>:
    size_hint_y: .85
    Label:
        halign: 'left'
        valign: 'middle'
        size_hint_y: None
        height: self.texture_size[1]
        text_size: self.width, None
        text: root.text_selected



<SelectableLabel>:
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
""")


class TextWrapper(BoxLayout):
    def __init__(self, text="", **kwargs):
        super(TextWrapper, self).__init__(**kwargs)
        self.content = self.wrap_text(text)

    def wrap_text(self, source):
        text = source.split("|")

        for i in range(0, len(text)):
            if "img=" in text[i]:
                self.add_widget(Image(source=text[i][4:]))
            else:
                self.add_widget(Label(text=text[i]))
        return text


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    pass


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableLabel, self).refresh_view_attrs(
            rv, index, data)

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected
        if is_selected:
            print("selection changed to {0}".format(rv.data[index]))
            rv.name_selected = rv.data[index]['text']
            rv.text_selected = rv.data[index]['description']
        else:
            print("selection removed for {0}".format(rv.data[index]))


class ScreenSpecies(Screen):
    pass


class SpeciesView(RecycleView):
    def __init__(self, **kwargs):
        super(SpeciesView, self).__init__(**kwargs)

        self.species_data = [
            {"text": "Test1", "description": "Test1.textbf |img=serveimage.png| Test1.textaf"},
            {"text": "Test2", "description": "Test2.text"},
            {"text": "Test3", "description": "Test3.text"}
        ]

        for species in self.species_data:
            species["description"] = TextWrapper(species["description"])
        # clean keywords out

        self.data = self.species_data


class SpeciesLabel(ScrollView):
    text_selected = StringProperty("")
    name_selected = StringProperty("")


screen_manager = ScreenManager()
screen_manager.add_widget(ScreenSpecies(name="screen_species"))


class TestApp(App):
    def build(self):
        return screen_manager


test_app = TestApp()
test_app.run()

此代码无效!因为你不能在不破坏程序的情况下点击标签,这就是问题所在。 text_selected只接受str

 ValueError: SpeciesView.text_selected accept only str

我不知道如何显示视图中每个元素描述中的TextWrapper。

这一切的目的是,我可以根据例如改变文本来改变文本。可选择的选项列表,每个文本中仍有图像 结果看起来应该类似于:

The Buttons are already done!

同样,问题是如何根据最后按下的按钮动态更改右侧文本并更改图像。

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

包木窗

我还没有在kivy找到任何特定的包装工具来解决你的问题,所以我为此编写了一个临时函数。理想情况下,我认为应该有一个TextMarkup选项允许在文本中嵌入图像,甚至是一个单独的小部件,但是现在这将比没有更好。

def wrap_text(target, source):
    text = source.split("|")

    for i in range(0, len(text)):
        if "img=" in text[i]:
            target.add_widget(Image(source=text[i][4:]))
        else:
            target.add_widget(Label(text=text[i]))

提供的代码示例可以假设您将图像引用放在字符串中,格式如下:|img=path|。例如:

class Main(App):

    def build(self):
        base = Builder.load_file("Main.kv")

        text_field = "This is text, below it should be the first image|img=example_1.png|" \
                     "Whereas this text should appear in between the images|img=example_2.png|" \
                     "And this text under the second image"

        wrap_text(base, text_field)

        return base

Main.kv 内容

BoxLayout:
    orientation: 'vertical'

当然,如果您愿意,可以引入一些改进,例如,您可以自行创建自定义Label,而不是添加常规\n。或者您可以使图像始终水平添加,除非rv.text_selected = rv.data[index]['description']字符位于图像引用之前的字符串中。或者您可以尝试扩展现有的kivy工具以包含您想要的功能(正如人们在kivy的Garden中所做的那样)。

从小部件中检索文本

rv.data[index]['description']不起作用,因为TextWrapperspecies["description"] = TextWrapper(species["description"])小部件,而不是字符串。请注意,您将其指定为以下行中的包装器:# Initialise a new, empty string, to add to it later text = "" # Iterate over widget's children (might as well dynamically assign them an id to find them faster) for widget in rv.data[index]['description'].walk(): # Add text if a label is found if isinstance(widget, Label): text += widget.text # Assign the text to the field rv.text_selected = text

如果要从窗口小部件中检索文本字段(实际上是窗口小部件中标签内的文本),请使用以下代码:

TextWrapper

输出:

Output after clicking one of the text fields

同样,您可以以相同的方式检索图像。但请注意,如果要同时显示文本和图像,则无需手动检索文本或图像,只需显示add_widget()即可。您可以通过调用BoxLayout例如Add-Type -AssemblyName PresentationFramework $window = New-Object Windows.Window $commonKeyEvents = { [System.Windows.Input.KeyEventArgs] $e = $args[1] if (($e.Key -eq "Q" -and $e.KeyboardDevice.Modifiers -eq "Ctrl") -or ($e.Key -eq "ESC")) { $this.Close() } } $window.Add_PreViewKeyDown($commonKeyEvents) $window.ShowDialog() | Out-Null 来添加它。

答案 1 :(得分:1)

要实现此目的,您必须创建自定义窗口小部件,首先创建Content,然后将BasedOn添加到Button。在FloatLayout中添加{ {1}}和Button 这是一个例子 的.py

FloatLayout

.kv文件

Image