Changing Recycle View Column Widths

452 views Asked by At

RecycleView is giving me some trouble, especially with changing the columns to a specific size. I coded the intended column width with a GridLayout above the RecycleView. I also made an odd way to select the whole row when one cell is selected by using modulos. I'm not sure if this is the best way, but I would love other's opinions on this method.

I will eventually want to be able to delete the entire row, and then the selected data inside the JSON file. But for right now, I just want the widths appropriately sized.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.storage.jsonstore import JsonStore
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior

kv_string = """

ScreenManager:
    id: manager

    Screen:
        BoxLayout:
            orientation: 'vertical'
            canvas:
                Color:
                    rgba: .2,.2,.5,1
                Rectangle:
                    pos: self.pos
                    size: self.size

            GridLayout:
                size_hint_y: .3
                cols:4
                MyButton:
                    text: 'Num'
                    size_hint_x: 0.5
                MyButton:
                    text: 'Ratings'
                MyButton:
                    text: 'Name'
                    size_hint_x: 2
                MyButton:
                    text: 'Score'
                    on_press:
                        #arrange the boxing in ascending or descending order

            RecycleView:
                data: [{'text': str(x)} for x in app.data]
                viewclass: 'SelectableLabel'

                SelectableRecycleGridLayout:
                    cols: 4
                    #default_size: None, dp(26)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    multiselect: True
                    touch_multiselect: True

            ToggleButton:
                id: toggle_button
                size_hint_y: .3
                text: 'Delete Selected'
                state: 'normal'
                on_press:
                    app.data = [0]

<SelectableLabel>:
    canvas.before:
        Color:
            rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size

<MyButton@Button>:
    background_color: 0,0,0,1
"""
class SelectableLabel(RecycleDataViewBehavior, Label):
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        self.index = index
        return super(SelectableLabel, self).refresh_view_attrs(
            rv, index, data)

    def on_touch_down(self, touch):
        if super(SelectableLabel, self).on_touch_down(touch):
            return True

        # *** This selects the whole row *** # Not sure if this is the best way.
        if self.collide_point(*touch.pos) and self.selectable:
            self.parent.select_with_touch(self.index, touch)
            if self.index % 4 == 0:
                self.parent.select_with_touch(self.index + 1)
                self.parent.select_with_touch(self.index + 2)
                self.parent.select_with_touch(self.index + 3)
                return
            elif self.index % 4 == 1:
                self.parent.select_with_touch(self.index + 1)
                self.parent.select_with_touch(self.index + 2)
                self.parent.select_with_touch(self.index -1)
                return
            elif self.index % 4 == 2:
                self.parent.select_with_touch(self.index + 1)
                self.parent.select_with_touch(self.index - 2)
                self.parent.select_with_touch(self.index -1)
                return
            elif self.index % 4 == 3:
                self.parent.select_with_touch(self.index - 1)
                self.parent.select_with_touch(self.index - 2)
                self.parent.select_with_touch(self.index - 3)
                return

    def apply_selection(self, rv, index, is_selected):
        self.selected = is_selected

        if App.get_running_app().root.ids.toggle_button.state == 'down':
            print('Deleted', index) #Still need to add delete function
            rv.layout_manager.clear_selection() #Not working
            self.remove_widget(index) #Also not working

class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior, RecycleGridLayout):
    pass

class MyApp(App):

    data = [] #ListProperty?

    store = JsonStore('file.json')
    store.put('Example: 1', value_1 = 'Rating: C', value_2 = 'Score: 10', value_3 = 'Name: Zack')
    store.put('Example: 2', value_1 = 'Rating: A', value_2 = 'Score: 32', value_3 = 'Name: Pete')
    store.put('Example: 3', value_1 = 'Rating: B', value_2 = 'Score: 24', value_3 = 'Name: Toby')
    store.put('Example: 4', value_1 = 'Rating: D', value_2 = 'Score: 03', value_3 = 'Name: Lars')

    x = 0

    for rows in store.keys():
        x += 1
        data.append(x)
        for row in store.get(rows):
            data.append(store.get(rows)[row])

    print(data) #shows successfully appended

    def build(self):
        root_widget = Builder.load_string(kv_string)
        return root_widget

if __name__ == "__main__":
    MyApp().run()
1

There are 1 answers

2
John Anderson On BEST ANSWER

Kind of an ugly hack, but you can add a refresh_view_layout method to your SelectableLabel class:

def refresh_view_layout(self, rv, index, layout, viewport):
    mod = index % 4
    if mod == 0:
        layout['size_hint'] = (0.15, None)
    elif mod == 1:
        layout['size_hint'] = (0.225, None)
    elif mod == 2:
        layout['size_hint'] = (0.225, None)
    elif mod == 3:
        layout['size_hint'] = (0.4, None)
    super(SelectableLabel, self).refresh_view_layout( rv, index, layout, viewport)

So, you can set the size_hint for each SelectableLabel to whatever you want. If you make the size_hint_x agree with the size_hint_x that is used for your Button column headers, I think you will get what you want.