微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Kivy RecycleView-图标已复制并放置在数据项中

如何解决Kivy RecycleView-图标已复制并放置在数据项中

我正在尝试基于KivyMD FileManager类构建gallery应用程序。布局由三列组成,图像放置在这些列中。

问题是,当我单击图像(它们是IconButtons)以在图像的左下角添加小点击图标时,在同一列中的其他图像上又添加了一些单击图标。我认为是因为布局在几个周期后重复了相同的实例。

Click icon added as the button (image) pressed
Unexpected click icon appears in another row (third row after the clicked row)

代码如下:


import os
import threading
import time

from os import listdir
from os.path import join,isfile
from pathlib import Path

import PIL
from PIL import ImageOps
from kivy import Logger
from kivy.app import App
from kivy.clock import mainthread
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import OptionProperty,ListProperty,BooleanProperty,StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.Boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import AsyncImage
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.uix.scrollview import ScrollView
from kivymd import images_path
from kivymd.app import MDApp
from kivymd.toast import toast
from PIL import Image as PILImage
from kivymd.uix.button import MDIconButton
from kivymd.uix.label import MDIcon
from kivymd.uix.toolbar import MDToolbar

Builder.load_string('''
#:import os os
<MyToolBar>:
    id: toolbar
    elevation: 10
    pos_hint:{'top':1}
    size_hint_y: 0.1
    md_bg_color: 0/255,176/255,240/255,1
    specific_text_color: 1,1,1
<RV>:
    id: rv
    key_viewclass: 'viewclass'
    key_size: 'height'
    bar_width: dp(4)
    bar_color: app.theme_cls.primary_color
    #on_scroll_stop: root._update_list_images()
    pos_hint: {'top':0.9}
    size_hint_y: 0.9
    
    RecycleBoxLayout:
        default_size: None,dp(500)
        default_size_hint: 1,None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
        
<LabelContent@MDLabel>
    size_hint_y: None
    height: self.texture_size[1]
    shorten: True
    shorten_from: 'center'
    halign: 'center'
    text_size: self.width,None
    
<BodyManagerWithPrevIoUs>
    id: bodymanager
    paths: []
    path: ''
    type: 'folder'
    events_callback: lambda x: None
    orientation: 'vertical'
    
    MDGridLayout:
        id: grid_Box
        cols: 3
        row_default_height: (self.width - self.cols * self.spacing[0]) / self.cols
        row_force_default: True
        adaptive_height: True
        padding: dp(4),dp(-4)
        spacing: dp(4),dp(4)
        #pos_hint: {'top':1}

        BoxLayout:
            orientation: 'vertical'
            IconButton:
                size_hint_y: None
                height: dp(300) if self.source and os.path.split(self.source)[1] == "folder.png" else root.width /3
                source: root.get_source(root.type,root.paths,1)
                on_release: root.events_callback(path=root.get_source(root.type,1),instance=self)
            MDIcon:
                icon: ''
                pos: self.parent.children[1].pos

        BoxLayout:
            orientation: 'vertical'
            IconButton:
                size_hint_y: None
                height: dp(300) if self.source and os.path.split(self.source)[1] == "folder.png" else root.width /3
                source: root.get_source(root.type,2)
                on_release: root.events_callback(path=root.get_source(root.type,2),instance=self)
            MDIcon:
                icon: ''
                pos: self.parent.children[1].pos
        BoxLayout:
            orientation: 'vertical'
            IconButton:
                size_hint_y: None
                height: dp(300) if self.source and os.path.split(self.source)[1] == "folder.png" else root.width /3
                source: root.get_source(root.type,3)
                on_release: root.events_callback(path=root.get_source(root.type,3),instance=self)
            MDIcon:
                icon: ''
                pos: self.parent.children[1].pos
''')


class IconButton(ButtonBehavior,AsyncImage):
    allow_stretch = Booleanproperty()
    clicked = Booleanproperty()


class MyToolBar(MDToolbar):
    pass


class RV(RecycleView):
    search = OptionProperty("all",options=["all","files"])
    ext = Listproperty()
    use_access = BooleanProperty(True)

    def __init__(self,path,**kwargs):
        super(RV,self).__init__(**kwargs)

        self.ext = [".png",".jpg",".jpeg"]
        self.app = MDApp.get_running_app()

        dirs,files = self.get_content(path)

        threading.Thread(target=self._create_prevIoUs,args=(path,)).start()

        split_files = self._split_list(files,3)

        manager_list = []
        app = MDApp.get_running_app()
        for list_files in list(split_files):
            manager_list.append(
                {
                    "viewclass": "BodyManagerWithPrevIoUs","path": path,"paths": list_files,"type": "files","height": app.root.width / 3,"events_callback": app.add_checkicon
                })
        self.data = manager_list
        #[{'source': x} for x in img_source]

    def count_ext(self,path):
        ext = os.path.splitext(path)[1]
        if ext != "":
            # print(self.ext)
            if ext.lower() in self.ext or ext.upper() in self.ext:
                return True
        return False

    def _create_prevIoUs(self,path):
        if "r" not in self.get_access_string(path):
            toast("PermissionError")
            return

        for image in os.listdir(path):
            _path = os.path.join(path,image)
            if os.path.isfile(_path):
                if self.count_ext(_path):
                    path_to_thumb = os.path.join(
                        '/home/username/Desktop',"thumb",f"thumb_{image}"
                    )
                    if not os.path.exists(path_to_thumb):
                        im = PILImage.open(_path)
                        im = ImageOps.fit(im,(200,200),method=0,bleed=0.0,centering=(0.5,0.5))
                        im.thumbnail((200,200))
                        im.save(path_to_thumb,"PNG")

    def get_access_string(self,path):
        access_string = ""
        if self.use_access:
            access_data = {"r": os.R_OK,"w": os.W_OK,"x": os.X_OK}
            for access in access_data.keys():
                access_string += (
                    access if os.access(path,access_data[access]) else "-"
                )
        return access_string

    def get_content(self,path):
        """Returns a list of the type [[Folder List],[file list]]."""
        print(path)
        try:
            files = []
            dirs = []
            onlyfiles1 = [join(path,f) for f in listdir(path) if isfile(join(path,f))
                          and f.endswith(('JPG','png',".jpeg"))]
            print(onlyfiles1)
            onlyfiles1.sort(key=os.path.getmtime)
            onlyfiles1.reverse()
            print(onlyfiles1)
            for each in onlyfiles1:
                content = each.split('/')[-1]
                if os.path.isdir(os.path.join(path,content)):
                    if self.search == "all" or self.search == "dirs":
                        dirs.append(content)
                else:
                    if self.search == "all" or self.search == "files":
                        if len(self.ext) != 0:
                            try:
                                if self.count_ext(content):
                                    files.append(
                                        os.path.join(
                                            '/home/username/Desktop',f"thumb_{content}",)
                                    )

                            except IndexError:
                                pass
                        else:
                            files.append(content)
            return dirs,files
        except OSError:
            self.history.pop()
            return None,None

    def _update_list_images(self):
        # self.refresh_from_viewport()
        self.refresh_from_layout()

    def _split_list(self,l,n):
        if l:
            n = max(1,n)
            return (l[i : i + n] for i in range(0,len(l),n))
        else:
            return []


class BodyManagerWithPrevIoUs(BoxLayout):
    def get_source(self,source_type,paths,index):
        if len(paths) >= index:
            source = paths[index - 1]
        else:
            source = f"{images_path}transparent.png"
        return source


class TestApp(MDApp):
    check_dict = {}
    iconcuk = Stringproperty()
    instance_nums = []

    def build(self):
        self.sm = ScreenManager()
        self.sm.add_widget(Screen(name='Screen 1'))
        self.sm.add_widget(Screen(name='Screen 2'))
        wid = self.sm.get_screen('Screen 1')
        wid.add_widget(Button(text='Screen 1',on_release=self.change))
        return self.sm

    def change(self,*kwargs):
        self.sm.current = 'Screen 2'
        self.s2 = self.sm.get_screen('Screen 2')
        self.s2.add_widget(MyToolBar())
        self.rv = RV(path = 'Directory Path')
        self.s2.add_widget(self.rv)


    def add_checkicon(self,instance):

        if instance.parent.children[0].icon == 'check-bold':
            if instance.source in self.check_dict:
                instance.parent.children[0].icon = ''
                self.check_dict.pop(path)
            print(self.check_dict)
        else:
            if instance.source not in self.check_dict:
                instance.parent.children[0].icon = 'check-bold'
                self.check_dict[path] = instance
            print(self.check_dict)


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

任何帮助都很感谢!

解决方法

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。