2

I am trying to select a file (picture), through a simple app I made using file manager, but I keep getting this error message: kivy.uix.widget.WidgetException: Cannot add <kivymd.uix.filemanager.filemanager.MDFileManager object at 0x000001D98DC5F890>, it already has a parent <kivy.uix.modalview.ModalView object at 0x000001D98DC5F4A0>, whenever I click on the button to access files (i.e to open directories). I don't know want is wrong, I don't even know what I am doing wrong.

I would appreciate any help thanks.

Here's my .py file

from kivy.factory import Factory
from kivy.uix.modalview import ModalView

from kivymd.uix.filemanager import MDFileManager
from kivymd.theming import ThemeManager
from kivymd.toast import toast


from kivymd.app import MDApp
from kivy.core.window import Window


Window.size = (300, 530)


class MainApp(MDApp):
    title = "File Manage"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.bind(on_keyboard=self.events)
        self.manager_open = False
        self.manager = None

    def build(self):
        self.theme_cls.primary_palette = 'Gray'
        self.theme_cls.primary_hue = '200'
        self.theme_cls.theme_style = 'Dark'

        return Factory.ExampleFileManager()

    def file_manager_open(self):
        if not self.manager:
            self.manager = ModalView(size_hint=(1, 1), auto_dismiss=False)
            self.file_manager = MDFileManager(
                exit_manager=self.exit_manager, select_path=self.select_path)
            self.manager.add_widget(self.file_manager)
            self.file_manager.show('/')  # output manager to the screen
        self.manager_open = True
        self.manager.open()

    def select_path(self, path):
        '''It will be called when you click on the file name
        or the catalog selection button.

        :type path: str;
        :param path: path to the selected directory or file;
        '''

        self.exit_manager()
        toast(path)

    def exit_manager(self, *args):
        '''Called when the user reaches the root of the directory tree.'''

        self.manager.dismiss()
        self.manager_open = False

    def events(self, instance, keyboard, keycode, text, modifiers):
        '''Called when buttons are pressed on the mobile device..'''

        if keyboard in (1001, 27):
            if self.manager_open:
                self.file_manager.back()
        return True


if __name__ == "__main__":
    app = MainApp()
    app.run()

and here's my .kv file:

<ExampleFileManager@BoxLayout>
    orientation: 'vertical'

    MDToolbar:
        id: progress_toolbar
        title: 'Progress'

    ScrollView:
        MDGridLayout:
            cols: 2
            adaptive_height: True
            spacing: (10, 15)
            padding: [25, 25]

            MDLabel:
                halign: 'center'
                text: 'Before'

            MDLabel:
                halign: 'center'
                text: 'Now'

            MDCard:
                ripple_behavior: True
                orientation: 'vertical'
                size_hint_y: None
                size: 120, 220
                elevation: 15
                radius: 8
                MDIconButton:
                    icon: "camera-outline"
                    user_font_size: "64sp"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_release: app.file_manager_open()

            MDCard:
                ripple_behavior: True
                orientation: 'vertical'
                size_hint_y: None
                size: 120, 220
                elevation: 15
                radius: 8
                MDIconButton:
                    icon: "camera-outline"
                    user_font_size: "64sp"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_release: app.file_manager_open()


            MDTextField:
                hint_text: 'Date'
                width: 100

            MDTextField:
                hint_text: 'Date'
                width: 100

            MDTextField:
                hint_text: 'Weight'
                width: 80

            MDTextField:
                hint_text: 'Weight'
                width: 80

Edit: New code:

from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.core.window import Window
from plyer import filechooser

Window.size = (300, 530)

KV = """
MDBoxLayout:
    orientation: 'vertical'

    MDToolbar:
        id: progress_toolbar
        title: 'Progress'

    ScrollView:
        MDGridLayout:
            cols: 2
            adaptive_height: True
            spacing: (10, 15)
            padding: [25, 25]

            MDLabel:
                halign: 'center'
                text: 'Before'

            MDLabel:
                halign: 'center'
                text: 'Now'

            MDCard:
                ripple_behavior: True
                orientation: 'vertical'
                size_hint_y: None
                size: 120, 220
                elevation: 15
                radius: 8
                MDIconButton:
                    icon: "camera-outline"
                    user_font_size: "24sp"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_release: app.file_chooser1()
                Image:
                    id: img1
                    allow_stretch: True
                    keep_ratio: False
                    # size_hint_y: .5

            MDCard:
                ripple_behavior: True
                orientation: 'vertical'
                size_hint_y: None
                size: 120, 220
                elevation: 15
                radius: 8
                MDIconButton:
                    icon: "camera-outline"
                    user_font_size: "24sp"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_release: app.file_chooser2()
                Image:
                    id: img2
                    allow_stretch: True
                    keep_ratio: False
                    # size_hint_y: .5


            MDTextField:
                hint_text: 'Date'
                width: 100

            MDTextField:
                hint_text: 'Date'
                width: 100

"""


class Example(MDApp):

    def build(self):
        return Builder.load_string(KV)

    def file_chooser1(self):
        filechooser.open_file(on_selection=self.selected1)

    def file_chooser2(self):
        filechooser.open_file(on_selection=self.selected2)

    def selected1(self, selection1):
        self.root.ids.img1.source = selection1[0]

    def selected2(self, selection2):
        self.root.ids.img2.source = selection2[0]


Example().run()
Edwin
  • 565
  • 11
  • 26

1 Answers1

3

Instead of using the FileManager widget, there is actually a better alternative which is from the plyer module, specifically the filechooser API. What it does is to open up the default file manager app of your device to either choose folder, file or save file. Here is an example:

def file_manager_open(self):
    from plyer import filechooser
    path = filechooser.open_file()[0] 
    # this method returns a list with the first index
    # being the path of the file selected
    toast(path)

More information regarding the APIs of this library here.

One tip I can give you is that because this file chooser is very weak on the Windows platform, I would recommend using tkinter's filedialog if you want.

Weebify
  • 561
  • 4
  • 13
  • Thanks a lot @weebify, That actually solves the prpblem. But after I select an image, the image doesn't appear on the screen.. please what I am doing wrong – Edwin Oct 23 '21 at 03:32
  • 1
    I believe it's because the `select_path` method of yours isn't called after choosing your desired image, thus not showing the image. I've edited my answer so that it should show up, check it out – Weebify Oct 23 '21 at 06:50
  • Thanks @Weebify, but this wasn't actually displays the image, although it displays the path, but my goal is to display an image on the 'MDCard'. I really appreciate your help so, Thanks a lot.. – Edwin Oct 23 '21 at 08:59
  • Thanks a lot for your help. I was able to find a video that about plyer, and I changed a lot of things in code. I've added the new code in my previous post. The image now displays, but I now two new issues, **1.** The orientation of the image changes when it is inputed, I would like the orientation to remain the same when it is inputed. **2.** I'd love to make the inputed image file be available, when it is opened again, so I need the inputed image file to be saved. Please how do I make these things happen. Thanks a lot for help, I really appreciate. – Edwin Oct 23 '21 at 10:44
  • 1
    I don't really know what to do to make your first request work out, but the second one: Just make a save folder somewhere, for example your home directory (you can get the path by using `plyer.storagepath.get_home_dir()`), and then save your input image there. After that just make a check if there is any image file(can be named with your preference in case someone messes with the folder) and if `True` then when opening the image again, it's available. Might not be the most optimal way, but at least it's the most simple way – Weebify Oct 23 '21 at 10:52