Search code examples
pythonkivy

how to select multiple Images in checkbox in recycle view Kivy?


 from kivy.uix.modalview import ModalView    
 from kivy.uix.screenmanager import Screen    
 from kivymd.app import MDApp   
 from kivy.metrics import dp    
 from kivy.lang.builder import Builder

KV = """ 
<ImgCard@ButtonBehavior+BoxLayout>
    path: ""
    orientation: "vertical"
    size_hint_y: None
    Image:
        source: root.path
        size_hint_y: .9
    MDCheckbox:
 
<Gallery> 
    orientation: 'vertical'
    ScreenManager:
    Screen:
        BoxLayout:
            orientation: 'vertical'
        RecycleView:
            id: img_base
            viewclass: "ImgCard"
            canvas.before:
                #$#Color:
                #$rgba: (.4, .4, .4, .7)
                Rectangle:
                    size: self.size
                    pos: self.pos
            RecycleGridLayout:
                spacing: 10 
                cols: 3
                default_size: None, dp(48)
                default_size_hint: 1, None
                size_hint_y: None
                height: self.minimum_height
                multiselect: True
                touch_multiselect: True     """
class ImageManager(ModalView):
    pass

class Gallery(Screen):

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

    self.manager_list = []
    self.dir = os.getcwd()
    self.available_image_format = ['.png', '.jpg', '.jpeg', '.bmp']  # etc

def load_images(self):
    if not self.manager_list:
        for image in os.listdir(self.dir):
            target_filename, target_file_extension = os.path.splitext(image)
            if target_file_extension in self.available_image_format:
                path_to_image = os.path.join(self.dir, image)
                self.manager_list.append(
                    {
                        "ImgCard": "ImageManager",
                        "path": path_to_image,
                        "height": dp(200),
                    }
                )
        self.ids.img_base.data = self.manager_list
        self.images=[self.dir]

class GalleryApp(MDApp):
    def build(self):
        Builder.load_string(KV)
        return Gallery()
    
    def on_start(self):
        self.root.load_images()
    

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

How to link checkbox in gallery for also multiple select in kivy. Can anyone help this?


Solution

  • If you are using MDCheckbox in a RecycleView, then you should include the state of that MDCheckbox in the data. If you don't do that, then the RecyclView doesn't know about the state of the MDCheckbox and when an MDCheckbox gets recycled, its state will be whatever it was last time it was used. To accomplish that, first modify your kv rule for ImgCard:

    <ImgCard@BoxLayout>
        path: ""
        selected: False  # added to indicate selection
        index: -1  # added to enable accessing this item in the data
        orientation: "vertical"
        size_hint_y: None
        Image:
            source: root.path
            size_hint_y: .9
        MDCheckbox:
            id: cb
            state: 'down' if root.selected else 'normal'
            on_release: app.root.adjust_data(root)
    

    Note that the ButtonBehavior is not necessary.

    Then, modify the initial data to initialize the new properties:

    def load_images(self):
        count = 0
        if not self.manager_list:
            for image in os.listdir(self.dir):
                target_filename, target_file_extension = os.path.splitext(image)
                if target_file_extension in self.available_image_format:
                    path_to_image = os.path.join(self.dir, image)
                    self.manager_list.append(
                        {
                            "ImgCard": "ImageManager",
                            "path": path_to_image,
                            "height": dp(200),
                            "selected": False,
                            "index": count
                        }
                    )
                    count += 1
            self.ids.img_base.data = self.manager_list
            self.images = [self.dir]
    

    And add a method in the Gallery class to update the data when a MDCheckbox is changed:

    def adjust_data(self, imgcard):
        rv = self.ids.img_base
        rv.data[imgcard.index]['selected'] = imgcard.ids.cb.state == 'down'
        imgcard.ids.cb.state = 'normal'
        rv.refresh_from_data()
    

    To remove the selected items, you can add a Button in your <Gallery> rule that calls:

    def delete(self, instance):
        rv = self.ids.img_base
        deleted = False
        for i in range(len(rv.data) - 1, -1, -1):
            item = rv.data[i]
            if item['selected']:
                del rv.data[i]
                deleted = True
        if deleted:
            self.adjust_indices()
    

    Since deleting any items will upset the index property of items in the data, another method is required to adjust the index properties:

    def adjust_indices(self):
        # adjust index values to account for removd items
        rv = self.ids.img_base
        index = 0
        for item in rv.data:
            item['index'] = index
            index += 1