Reputation: 21
I am trying to build a function that helps the user select/unselect all viewclass-instances in a RecycleView.
The Problem is that while i can change the "selected" attribute of every viewclass in the RecycleView to True or False.
like this for example:
for viewclass in recycle_view.data:
viewclass['selected'] = False
-the "selected" color of the viewclasses do not update themselves, meaning it retains the "selected" color, eventhough it's technically unselected. I wonder how i can refresh the recycleview in a way, that it also the takes the color of the viewclasses into account.
This is the code that handles the creation of the vieclasses "UserCard", with all it's selecting methods:
class UserCard(RecycleDataViewBehavior, MDBoxLayout):
selected_cards = False
name = StringProperty()
price = StringProperty()
date = StringProperty()
category = StringProperty()
callback = ObjectProperty(lambda x: x)
rview = ObjectProperty()
index = None
current_card_index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
selected_list_cards = []
selected_list_index = []
num_selected_cards = 0
color_normal = (18/250, 18/250, 18/250, 1)
color_select = (25/250, 25/250, 25/250, 1)
def refresh_view_attrs(self, rv, index, data):
self.index = index
self.selected = data["selected"]
return super().refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
if super().on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
Clock.schedule_once(self.callback)
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
self.selected = is_selected
rv.data[index]["selected"] = is_selected
screen_products = MDApp.get_running_app().root.get_screen('float_layout_products')
if is_selected:
rv.data[index]['md_bg_color'] = (25/250, 25/250, 25/250, 1)
if index not in UserCard.selected_list_index:
UserCard.selected_list_index.append(index)
UserCard.current_card_index = index
if index not in UserCard.selected_list_cards:
UserCard.selected_list_cards.append(self)
else:
rv.data[index]['md_bg_color'] = (18/250, 18/250, 18/250, 1)
if index in UserCard.selected_list_index:
UserCard.selected_list_index.remove(index)
if index in UserCard.selected_list_cards:
UserCard.selected_list_cards.remove(self)
screen_products.ids.items_selected.text = str(
len(UserCard.selected_list_index))
def on_tap_card(self, *args):
screen_products = MDApp.get_running_app().root.get_screen('float_layout_products')
recycle_view = screen_products.ids.recycle_view
datas = [data["selected"] for data in recycle_view.data]
if True in datas and not UserCard.selected_cards:
screen_products.switch_topbar_select()
UserCard.selected_cards = True
else:
if len(list(set(datas))) == 1 and not list(set(datas))[0]:
UserCard.selected_cards = False
for index, is_selected in enumerate(datas):
num_selected_cards = datas.count(True)
if num_selected_cards == 1 and is_selected:
UserCard.selected_cards = True
screen_products.ids.action_button_bottom.icon = 'pencil'
screen_products.on_edit_true()
elif num_selected_cards == 0 and not is_selected:
UserCard.selected_cards = False
elif num_selected_cards > 1 and is_selected:
# UserCard.selected_cards = False
screen_products.ids.action_button_bottom.icon = 'plus'
screen_products.on_edit_false()
if not UserCard.selected_cards:
screen_products.ids.action_button_bottom.icon = 'plus'
screen_products.on_edit_false()
screen_products.switch_topbar_search()
num_selected_cards = 0
def load_usercards_products(self):
screen_products = MDApp.get_running_app().root.get_screen('float_layout_products')
recycle_view = screen_products.ids.recycle_view
visual_path = fr'C:\Python\Projects\MyApp\saved_products\visuals\saved_visuals'
input_dir = 'C:/Python/Projects/MyApp/saved_products/inputs/'
input_files = [f for f in os.listdir(
input_dir) if os.path.isfile(os.path.join(input_dir, f))]
num_inputs = len(input_files)
creation_date_list = []
title_list = []
price_list = []
category_list = []
screen_products.ids.action_button_bottom.icon = 'plus'
async def generate_card():
recycle_view.data = []
for filename in os.listdir(visual_path):
if filename.endswith('.json'):
file_path = os.path.join(visual_path, filename)
with open(file_path, 'r') as f:
data = json.load(f)
creation_date = data['creation_date']
creation_date_list.append(creation_date)
title = data['title_text']
title_list.append(title)
price = data['price_value']
price_list.append(price)
category = data['category_value']
category_list.append(category)
for i in range(num_inputs):
await asynckivy.sleep(0)
recycle_view.data.append(
{
"name": title_list[i],
"price": price_list[i],
"date": creation_date_list[i],
"category": category_list[i],
"selected": False,
"callback": UserCard.on_tap_card,
}
)
Clock.schedule_once(lambda x: asynckivy.start(generate_card()))
These are the UserCard- and recycle_view classes.
<UserCard>
orientation: "vertical"
adaptive_height: False
md_bg_color: (25/250, 25/250, 25/250, 1) if self.selected else (18/250, 18/250, 18/250, 1)
radius: 16
padding: 0, 0, 0, "16dp"
MDRelativeLayout:
MDLabel:
id: name_label
text: root.name
adaptive_size: False
color: (51/250, 54/250, 63/250, 1)
pos: "12dp", "12dp"
pos_hint: {"top": 1.25, "left": 1}
bold: True
MDLabel:
id: price_label
text: root.price
adaptive_size: True
color: (51/250, 54/250, 63/250, 1)
pos: "12dp", "12dp"
pos_hint: {"top": 0.9, "center_x": 0.9}
opacity: 0
MDLabel:
id: date_label
text: root.date
adaptive_size: True
color: (51/250, 54/250, 63/250, 1)
pos: "12dp", "12dp"
pos_hint: {"center_y": 0.1, "left": 1}
bold: False
font_size: 13
MDFillRoundFlatButton:
id: category_ellipse
size_hint: (0, 0)
text: root.category
color: (51/250, 54/250, 63/250, 1)
font_size: 15
bold: True
icon_size: 15
icon: "gift-outline"
text_color: (51/250, 54/250, 63/250, 1)
md_bg_color: (23/250, 23/250, 23/250, 1)
pos: "12dp", "12dp"
pos_hint: {"right": 0.95, "center_y": 0.2}
_no_ripple_effect: True
radius: [20]
opacity: 0
<RView@RecycleView>
viewclass: 'UserCard'
size_hint: (0.95, 0.88)
pos_hint: {'center_x': 0.5, 'y': 0.11}
bar_color: 0,0,0,0
SelectableRecycleGridLayout:
orientation: 'vertical'
spacing: "16dp"
padding: "16dp"
default_size: None, dp(100)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: True
touch_multiselect: True
I have tried changing the md_bg_color of each viewclass, but that didn't help either, because it only changed the color of the viewclasses that were loaded. I've tried refreshing the recycleview via
recycle_view.refresh_from_data()
Also the color of the viewclasses did not go back to the "normal" color sometimes, instead those viewclasses were recycled and made other viewclasses, that were't selected, look like they were selected.
Upvotes: 0
Views: 66
Reputation: 35
You should provide minimal code so that people that want to help you out, can just copy paste and run the code (instead writing the code back and match the code that you gave - unless they have time / professional programmers where they can solve just by reading the short code given)
Below some other way to do what you want, or maybe not:
(if you have any questions regarding to this code, call me out on Kivy Discord)
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty, BooleanProperty, ListProperty
from kivy.uix.behaviors import ButtonBehavior
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.screen import MDScreen
from kivymd.utils import asynckivy
Builder.load_string('''
<Home>
app: app
RecycleView:
id: recycle_view
viewclass: 'UserCard'
bar_color: 0,0,0,0
RecycleBoxLayout:
orientation: 'vertical'
spacing: "16dp"
padding: "16dp"
default_size: None, dp(70)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
Button:
size_hint_y: .1
text: 'CLEAR'
on_press: root.clear_selected_user_card()
<UserCard>
orientation: "vertical"
size_hint: 1, None
md_bg_color: 'orange'
on_press: root.selected_node()
MDLabel:
id: name_label
text: root.name
bold: True
halign: 'center'
''')
class UserCard(ButtonBehavior, MDBoxLayout):
name = StringProperty()
selected = BooleanProperty(False)
def selected_node(self):
if self.selected:
self.selected = False
else:
self.selected = True
def on_selected(self, grid, selected):
if selected:
self.md_bg_color = 'red'
self.main_screen.update_selected_list(self, 'append')
else:
self.md_bg_color = 'orange'
self.main_screen.update_selected_list(self, 'remove')
class Home(MDScreen):
selected_data_list = ListProperty()
def update_selected_list(self, instance, task):
if task == 'append':
self.selected_data_list.append(instance)
else:
self.selected_data_list.remove(instance)
def clear_selected_user_card(self):
self.app.load_usercards_products()
class TestApp(MDApp):
def build(self):
return Home(name='home_screen')
def on_start(self):
Clock.schedule_once(self.load_usercards_products, 1)
def load_usercards_products(self, dt=None):
recycle_view = self.root.ids.recycle_view
title_list = ['title1', 'title2', 'title3', 'title4', 'title5']
async def generate_card():
recycle_view.data = []
num_inputs = 5
for i in range(num_inputs):
await asynckivy.sleep(0)
recycle_view.data.append(
{
"name": title_list[i],
"selected": False,
"main_screen": self.root
}
)
Clock.schedule_once(lambda x: asynckivy.start(generate_card()))
TestApp().run()
Upvotes: 0