python – How to force item in RecycleView to show as selected in Kivy?

I have a recycleview of several thousand songs. I want to force a selection when I shuffle songs. I can force scrolling to the selected song by setting scroll_y but I cannot seem to get the item in the recycleview to display as selected as with the basic kivy recycleview example where you click on an item and it changes background color.

I have a basic example. If you click it should start the random item selection every 0.5 sec and print out the rv.data showing that the items have ‘selected’: True.

I’ve tried setting key_selection: ‘selected’ and I’ve also tried setting ‘selected’: True in the recycleview.data.

MPROSRVbug.kv

<Manachan>:
    id: manachanID
    SelectionScreen:
        id: selectscreenID
        name: 'selectscreen'
        manager: 'manachanID'

<SelectionScreen>:
    id: selectionscreenid
    BoxLayout:
        orientation: 'horizontal'
        Video:
            id: previewVideo
        SongList:
            id: songlistid

<SongList>
    cols: 1
    size_hint_x: 1
    
    BoxLayout:
        size_hint: (1,.1)
        orientation: 'vertical'
    
        Label:
            id: songtitle
            canvas.before:
                Color:
                    rgba: (.3, .3, .3, 1)
                Rectangle:
                    pos: self.pos
                    size: self.size
    RV:
        id: recycle_grid1
        size_hint_x: 1
    
<RV>:
    viewclass: 'SongLinkButton'
    SelectableRecycleBoxLayout:
        id: recycle_grid2
        default_size: None, dp(120)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

<SongLinkButton>:
    id: songbutton
    orientation: 'horizontal'
    height: '80dp'
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    Label:
        id: textinfo
        text_size: self.width, None
        

MPROSRVbug.py

#MUSIC PLAYER RANDOM ORDERED SORT
import os, sys
from random import *

songDict = {}
songVal = {}

i = 0
for mp3file in ["1","9","7k","r","j","i","7","g","a","2",]:
    songDict[i] = mp3file
    songVal[str(mp3file)] = 0
    i += 1
    if i > 7:
        break
    
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.views import RecycleKVIDsDataViewBehavior 
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import *
from kivy.core.audio import SoundLoader
from kivy.uix.scrollview import ScrollView
from kivy.clock import Clock
import traceback

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        #set self.data:
        self.data = [{'textinfo.text': x, 'selected': False} for x in [songDict[y] for y in songDict]]
        print("self.data item?", self.data[0], len(self.data))

    def scroll_to_index(self, index):
        box = self.children[0]
        pos_index = (box.default_size[1] + box.spacing) * index
        scroll = self.convert_distance_to_scroll(
            0, pos_index - (self.height * 0.5))[1]
        if scroll > 1.0:
            scroll = 1.0
        elif scroll < 0.0:
            scroll = 0.0
        self.scroll_y = 1.0 - scroll

    def NextSong(self, *args):
        rvRef = App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"]
        randPickInt = randint(0,len(rvRef.data)-1)
        
        for d in rvRef.data:
            if d['textinfo.text'] == songDict[randPickInt]:
                d['selected'] = True
                dictElement = d
        target = os.path.normpath(rvRef.data[randPickInt]['textinfo.text'])
        App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
        
        targettextOG = rvRef.data[randPickInt]['textinfo.text']
        #PLAN: apply_selection always triggers when reycycleview refreshes, so set a global var of the selected guy. within the widget, if the name is the same as the selected guy, make yourself selected
        global curSelectedSong
        curSelectedSong = rvRef.data[randPickInt]['textinfo.text']
        
        counter = 0
        for d in rvRef.data:
            #remove all previously selected
            if d['selected']:
                d['selected'] = False
            #if we found the song, change to selected
            if d['textinfo.text'] == targettextOG:
                #print("VERSING", d['textinfo.text'], targettextOG, d['textinfo.text'] == targettextOG)
                d['selected'] = True
                #print("so has d updated?", d)
                rvRef.data[counter] = d
                print("updated rvRef.data", rvRef.data[counter])
            counter += 1
        print("WHAT IS RV DATA?", rvRef.data)
        App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].scroll_to_index(randPickInt)
        rvRef.refresh_from_data()

class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                             RecycleBoxLayout):

    ''' Adds selection and focus behaviour to the view. '''
    def __init__(self, **kwargs):
        super(SelectableRecycleBoxLayout, self).__init__(**kwargs)

def WeightedChoice():
    pass
    
class SongLinkButton(RecycleKVIDsDataViewBehavior,BoxLayout,Label):
    
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    
    def __init__(self, **kwargs):
        super(SongLinkButton, self).__init__(**kwargs)        

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

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SongLinkButton, 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):
        global curSelectedSong
        
        try:
            if curSelectedSong == rv.data[index]["textinfo.text"]:
                self.selected == True
        except:
            pass
        
        global soundObj
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected
        if is_selected and App.get_running_app().root.current == 'selectscreen':
            target = os.path.normpath(rv.data[index]['textinfo.text'])
            Clock.schedule_interval(App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].NextSong, 1)
            App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
        else:
            pass

class Manachan(ScreenManager):
    pass

class SongList(GridLayout):
    pass

class SelectionScreen(Screen):
        pass
    
class MyApp(App):

    def build(self):
        self.load_kv('MPROSRVbug.kv')
        self.title = "Music Player Random Ordered Sort by Pengindoramu"
        return Manachan()

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

Leave a Comment