Невозможно изменить данные ByteFitImage после того, как они определены

#python #python-3.x #kivy #kivy-language

Вопрос:

У меня есть приложение для музыкального проигрывателя, которое читает обложки песен и отображает их на экране. Для этого я создал свой собственный класс ByteFitImage, чтобы изображение считывало байты и адаптировалось к различным размерам.

Проблема, с которой я сталкиваюсь, заключается в том, что, если я решу изменить данные, я получу ключевую ошибку:

 KeyError: 'source'
 

И, очевидно, эта строка вызывает проблему:

 self.kv.ids.img.data = BytesIO(second_img.read())
 

Почему он запрашивает источник? Мой Код:

 from io import BytesIO
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty, VariableListProperty, Property
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.app import App


class ByteFitImage(BoxLayout):
    data = Property(None)
    radius = VariableListProperty([0], length=4)
    mipmap = BooleanProperty(False)
    _container = ObjectProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self._late_init)

    def _late_init(self, *args):
        self._container = Container(self.data)
        self.bind(data=self._container.setter("source"))
        self.add_widget(self._container)

    def reload(self):
        self._container.image.reload()


class Container(Widget):
    # source = ObjectProperty()
    image = ObjectProperty()

    def __init__(self, source, **kwargs):
        super().__init__(**kwargs)
        # self.image = AsyncImage(mipmap=mipmap)
        self.bind(size=self.adjust_size, pos=self.adjust_size)
        self.image = Image(source, ext="jpg")
        self.image.bind(on_load=self.adjust_size)
        # self.source = source

    def on_source(self, instance, value):
        if isinstance(value, str):
            self.image.source = value
        else:
            self.image.texture = value
        self.adjust_size()

    def adjust_size(self, *args):
        if not self.parent or not self.image.texture:
            return

        (par_x, par_y) = self.parent.size

        if par_x == 0 or par_y == 0:
            with self.canvas:
                self.canvas.clear()
            return

        par_scale = par_x / par_y
        (img_x, img_y) = self.image.texture.size
        img_scale = img_x / img_y

        if par_scale > img_scale:
            (img_x_new, img_y_new) = (img_x, img_x / par_scale)
        else:
            (img_x_new, img_y_new) = (img_y * par_scale, img_y)

        crop_pos_x = (img_x - img_x_new) / 2
        crop_pos_y = (img_y - img_y_new) / 2

        subtexture = self.image.texture.get_region(
            crop_pos_x, crop_pos_y, img_x_new, img_y_new
        )

        with self.canvas:
            self.canvas.clear()
            Color(1, 1, 1)
            Rectangle(texture=subtexture, pos=self.pos, size=(par_x, par_y))


class Example(App):
    def __init__(self, **kwargs):
        with open("images/song_img.jpg", "rb") as song_img:
            self.song_img_bytes = BytesIO(song_img.read())
        super(Example, self).__init__(**kwargs)
        self.kv = Builder.load_string('''
#:kivy 2.0.0
BoxLayout:
    orientation: "vertical"
    padding: 10
    ByteFitImage:
        id: img
        data: app.song_img_bytes
    Button:
        text: "Change cover!"
        font_size: 32
        on_release: app.change_cover()''')

    def build(self):
        return self.kv

    def change_cover(self):
        with open("images/second_img.jpg", "rb") as second_img:
            self.kv.ids.img.data = BytesIO(second_img.read())


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

Комментарии:

1. Вероятно, из-за вашей привязки: ` self.bind(данные=self._container.setter(«источник»)`’.

2. @ДжонАндерсон, я удалил строку, но изображение все равно не изменится