如何解决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()
任何帮助都很感谢!
解决方法
这是RecycleView小部件的标准行为-https://kivy.org/doc/stable/api-kivy.uix.recycleview.html#module-kivy.uix.recycleview
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。